I have a fun little weather app. For only $99/day, the app will check the weather daily, and if it's raining in Seattle, send an umbrella to the people of San Diego.
I use these two functions as part of my app:
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
}
I want to test IsRaining()
and SendUmbrella()
, but I don't want to have to actually send someone an umbrella every time I run my tests; my engineers use TDD and I do have a budget, you know. Same thing with IsRaining()
, what if the internet is down? I still need to be able to run by tests, rain or shine.
I want to do this in such a way that the code stays ergonomic and readable, but I definitely need to be able to test those HTTP-dependent functions. What's the most idiomatic way to do this in Go?
P.S. I'm using Testify. Tell me all about how I just lost any hope of idiomatic Go in the comments :)
I don't know about "most idiomatic", but same as in any other language hard coded
classespackages are a headache. Instead of calling methods directly on the http package, make an httpClient interface. Then mock the httpClient interface.You could pass the httpClient into the function, but it makes more sense to turn these into methods on a struct.
Then a mocked httpClient can be put into your umbrellaGiver.
See Mocking HTTP Requests in Golang