Как издеваться над промежуточным программным обеспечением, которое делает внешний вызов в тесте Golang?

TLDR: существует промежуточное ПО go-chi, которое выполняет внешний вызов для авторизации запроса. Мне нужно издеваться над тем, что он делает.

Подробности:

У меня есть этот тест:

func Test_HTTP_UserSet_Create_InvalidAction(t *testing.T) {
    requestBody :=
        `{
            "name": "test",
            "action": "TEST"
        }`

    req, _ := http.NewRequest("POST", "/1234/users", bytes.NewBuffer([]byte(requestBody)))

    recorder := setupInvalidActionRecorder(t, req)

    if status := recorder.Code; status != http.StatusBadRequest {
        t.Errorf("Status code expected to be %d but got %d", http.StatusBadRequest, status)
        return
    }
}

Вышеупомянутое setupUnknownErrorRecorder:

func setupUnknownErrorRecorder(t *testing.T, request *http.Request) *httptest.ResponseRecorder {
    recorder := httptest.NewRecorder()

    c := resty.New()

    resource := users.NewResource(
        httpClient.NewHttpClient(log, &config.Server{}, &logger.LogRecord{}, c),
    )

    resource.Routes().ServeHTTP(recorder, request)

    return recorder
}

Результат, который я получаю:

=== RUN   Test_HTTP_UserSet_Create_InvalidAction
    invalidAction_test.go:71: Status code expected to be 400 but got 401
--- FAIL: Test_HTTP_UserSet_Create_InvalidAction (0.00s)

Этот результат ожидается, так как сервер выполняет внешний вызов, используя клиент c, который я передаю NewResource.

Существует промежуточное ПО go-chi, которое использует этот клиент для выполнения внешнего вызова.

Мой вопрос: как я могу заставить этот http-вызов всегда возвращать 200 без действительного вызова. Ака как издеваться над этим звонком?

Редактировать: Интерфейс httpClient выше:

type HttpClient struct {
    logger          *logger.Logger
    config          *config.Server
    instrumentation *instrumentation
    record          *logger.LogRecord
    restyClient     *resty.Client
}

func NewHttpClient(
    logger *logger.Logger,
    config *config.Server,
    record *logger.LogRecord,
    restyClient *resty.Client,
) HttpClient {
    return HttpClient{
        instrumentation: newInstrumentation(logger, record),
        config:          config,
        logger:          logger,
        record:          record,
        restyClient:     restyClient,
    }
}
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
0
0
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Очень простое правило тестируемого кода гласит, что вы не можете просто создавать новые сервисы внутри других ваших сервисов. Вам нужно внедрить его и подготовить интерфейс, если его еще нет, для того, что возвращается resty.New. Таким образом, вы можете ввести свой макет в тесты.

Затем вы можете использовать, например, https://pkg.go.dev/github.com/stretchr/testify/mock для создания макетов и указания того, каким должно быть возвращаемое значение вашей имитируемой службой.

Обновление после комментария:

type HttpClient struct {
    logger          *logger.Logger
    config          *config.Server
    instrumentation *instrumentation
    record          *logger.LogRecord
    restyClient     *resty.Client // <- this is the thing you want to change from a pointer to resty.Client to an interface
}

Проверьте, какие методы restyClient вы используете в коде, и создайте новый интерфейс, содержащий их, например:

type MyRestyClient interface {
   FirstUsedMethod(..)
   
   ...
}

и замените объявление restyClient на

type HttpClient struct {
    ...
    restyClient     MyRestyClient

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

Позже вы просто настраиваете макет в тестах:

restyMock := new(RestyMock)
restyMock.On("MethodYouExpect").Return(returnValueOfYourChoice)

и вы вводите подготовленный mock в свой тестируемый сервис.

Спасибо за ответ! У меня есть интерфейс для службы. Я добавил код интерфейса к вопросу. Вы можете видеть, где находится *resty.Client. Но я не знаю, как создать новый экземпляр службы с издевательским *resty.Client в нем. Можете ли вы провести здесь?

Lee Moe 23.03.2022 17:18

Отредактировал мой ответ, так как он дает больше контекста вопросу.

Rafał Mnich 24.03.2022 14:14

Благодарю вас! Но, пожалуйста, потерпите меня. В какой момент можно использовать настоящий клиент Resty resty.New()? Я сделал то, что вы объяснили, но когда я использую это в простом httpClientInst := httpClient.NewHttpClient(log, &config, rec, resty.New()), я получаю обычную ошибку типа cannot use resty.New() (value of type *resty.Client) as *httpClient.MyRestyClientInterface value in argument to httpClient.NewHttpClient. В конце концов, весь смысл в использовании метода настоящей библиотеки.

Lee Moe 25.03.2022 01:35

Что ж, если вы хотите протестировать другой сервис, вам следует смоделировать все зависимости. Вероятно, клиент, который вы используете, уже был протестирован, так зачем еще раз его тестировать? Вместо этого просто используйте макет для своих тестов, чтобы проверить свой HttpClient.

Rafał Mnich 25.03.2022 09:13

Верно. Я близок к полному пониманию этого. Большое спасибо!

Lee Moe 25.03.2022 11:51

Другие вопросы по теме