Как протестировать / рефакторинг для тестирования функции, которая вызывает http.ListenAndServe - PullRequest
0 голосов
/ 16 апреля 2019

Я учусь идти и работаю над простым сервисом, который получает некоторые данные из очереди и помещает их в базу данных. Он также запускает веб-сервер, чтобы разрешить очистку данных. Сейчас у меня есть два файла go (для краткости опущен текст):

func main() {
    parseConfig()

    s := &Service{ServiceConfig: config}
    err := s.Run()
    if err != nil {
        panic(err)
    }
}

А потом определение сервиса (опять же опущены некоторые кусочки для краткости):

func (s *Service) Run() error {

    if err := s.validate(); err != nil {
        return err
    }

    if err := s.initDB(); err != nil {
        return err
    }
    defer s.db.Close()

    // Same pattern with health check library (init, start, close)
    // Same pattern starting queue consumer (init, start, close)

    s.mux = http.NewServeMux()
    s.registerHandlers(s.mux)

    http.ListenAndServe(":8080", s.mux)

    return nil
}

И структура

type Service struct {
    Config // Hold db connection info
    db  *sql.DB
    hc  *health
}

Я могу нормально тестировать отдельные части (например, initDB или validate), но мне неясно, как можно протестировать функцию Run, потому что http.ListenAndServe блокирует. Я в конечном итоге тайм-аут. Раньше я использовал httpTest и делал тестовый сервер, но это было тогда, когда main запускал сервер (поначалу приложение было более простым).

Некоторые вещи, которые я бы протестировал:

То, что я могу попасть в конечную точку метрики после запуска. Что я могу достичь конечной точки здоровья после того, как начал. То, что я могу отправить сообщение в очередь, и оно получено после запуска. Это Run на самом деле начинается без паники.

Некоторые примечания: я использую докер для раскрутки очереди и базы данных. Смысл тестирования функции Run заключается в том, чтобы убедиться, что начальная загрузка работает и приложение может успешно работать. В конце концов я захочу протолкнуть данные через очередь и утверждать, что они были обработаны правильно.

Вопрос: Как мне протестировать это или реорганизовать его так, чтобы его было легче тестировать сквозным?

1 Ответ

1 голос
/ 16 апреля 2019

Вы можете построить тестовый жгут, используя goroutine для выполнения Run в вашем модульном тесте:

func TestRun(t *testing.T) {
    service := Service{}
    serviceRunning := make(chan struct{})
    serviceDone := make(chan struct{})
    go func() {
        close(serviceRunning)
        err := service.Run()            
        defer close(serviceDone)
    }()

    // wait until the goroutine started to run (1)
    <-serviceRunning

    //
    // interact with your service to test whatever you want
    //

    // stop the service (2)
    service.Shutdown()

    // wait until the service is shutdown (3)
    <-serviceDone
}

Это просто базовый пример, чтобы показать, как это можно сделать в принципе.Есть несколько моментов, которые должны быть улучшены для использования в производстве:

(0) Наиболее важные детали: Не используйте http.ListenAndServe в производстве! Создайте свой собственный экземпляр http.Serverвместо.Это избавит вас от многих проблем.

s.httpServer := http.Server {
    Addr: ":8080",
    Handler: s.mux,
}

(1) Указание на то, что служба запущена, должно быть переведено в тип Service.Часть инициализации в Run может занять некоторое время, и канал индикации должен быть закрыт непосредственно перед вызовом ListenAndServe:

close(s.Running)
s.httpServer.ListenAndServe()

Конечно, вам необходимо добавить канал индикации к вашему типу услуги.

(2) Добавить метод Shutdown к Service, который вызывает s.httpServer.Shutdown().Это приведет к тому, что вызов s.httpServer.ListenAndServe вернется с ошибкой http.ErrServerClosed.

if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
    return err
}
return nil

Важно завершить работу службы в конце теста.В противном случае вы не сможете пройти более одного юнит-теста для вашего сервиса.И в любом случае это хорошая гражданская позиция для очистки ресурсов.

(3) Вам нужно подождать, пока не вернется service.Run, чтобы убедиться, что служба фактически закрыта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...