1

我有一个有趣的小天气应用程序。只需 99 美元/天,该应用程序就会每天检查天气,如果西雅图下雨,就给圣地亚哥的人们送一把雨伞。

我将这两个函数用作我的应用程序的一部分:

func IsRaining() (bool, error) {
    resp, err := http.Get("https://isitraining.in/Seattle")
    if err != nil {
        return false, fmt.Errorf("could not fetch raining status: %w", err)
    }

    parsed, err := weather.Parse(resp)
    if err != nil {
        return false, fmt.Errorf("could not parse the weather: %w", err)
    }

    return parsed.IsRaining, nil
}

func SendUmbrella() error {
    postData := umbrellaPostData()
    resp, err := http.Post("https://amazon.com", "text/html", &postData)
    if err != nil {
        return fmt.Errorf("could not send umbrella: %w", err)
    }
    return nil
}

我想测试IsRaining()and SendUmbrella(),但我不想每次运行测试时都给别人送伞;我的工程师使用 TDD,我确实有预算,你知道的。同理IsRaining(),万一断网了怎么办?我仍然需要能够通过测试运行,风雨无阻。

我想以这样一种方式来做到这一点,即代码保持符合人体工程学和可读性,但我绝对需要能够测试那些依赖于 HTTP 的函数。在 Go 中最惯用的方法是什么?

PS我正在使用Testify。在评论中告诉我所有关于我如何失去对惯用 Go 的任何希望 :)

4

1 回答 1

1

我不知道“最惯用的”,但与任何其他语言一样,硬编码包令人头疼。不要直接在 http 包上调用方法,而是创建一个 httpClient 接口。然后模拟 httpClient 接口。

您可以将 httpClient 传递给函数,但将它们转换为结构上的方法更有意义。

// Set up an interface for your http client, same as http.Client.
type httpClient interface {
    Get(string) (*http.Response, error)
}

// Make a struct to hang the client and methods off of.
type umbrellaGiver struct {
    client httpClient
}

// A cut down example method.
func (giver umbrellaGiver) getExample() ([]byte, error) {
    resp, err := giver.client.Get("https://example.com")
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    return io.ReadAll(resp.Body)
}

然后可以将一个模拟的 httpClient 放入您的雨伞提供者中。

// Our mocked client.
type mockedClient struct {
    mock.Mock
}

// Define the basic mocked Get method to record its arguments and
// return its mocked values.
func (m mockedClient) Get(url string) (*http.Response, error) {
    args := m.Called(url)
    if args.Get(0) == nil {
        return nil, args.Error(1)
    } else {
        return args.Get(0).(*http.Response), args.Error(1)
    }
}

func main() {
    // Make a mockedClient and set up an expectation.
    client := new(mockedClient)

    // Make an umbrellaGiver which uses the mocked client.
    s := umbrellaGiver { client: client }

    // Let's test what happens when the call fails.
    client.On(
        "Get", "https://example.com",
    ).Return(
        nil, errors.New("The system is down"),
    )

    body, err := s.getExample()
    if err != nil {
        panic(err)
    }
    fmt.Printf("%s", body)
}

请参阅在 Golang 中模拟 HTTP 请求

于 2021-04-28T05:14:08.540 回答