liveBook · Manning

Test double

As explained in Shipping Go: Develop, deliver, discuss, design, and go again

A fake is a simplified test double that stands in for an external object, service, or system while providing limited capabilities.

When a client calls an API you do not control, a fake lets you test the client without depending on that external service. A fake is a limited-capability object, struct, or service that stands in for another service. section 6.6

For an API client, a local HTTP test server can simulate the external system. Define the expected API contract, then use a handler to capture the request and return test responses. This lets you check success and error handling before integration. section 6.6

A translation client

A Go test server can route requests to a handler that calls a mock translation service. Configure the mock to return a translation or an error, then assert how the client responds. section 6.6

Fakes can provide higher-fidelity interactions than simpler test doubles, but they can be complicated to write and maintain. If a fake or its surrounding tests become unwieldy, reconsider whether the design can be simplified. Because the real external API may change, monitor and log failures; more advanced patterns such as circuit breakers are outside this treatment. section 6.7 section 6.6

Listing 6.22 client_test.go
func (suite *HelloClientSuite) SetupSuite() {                             #1
    suite.mockServerService = new(MockService)
    handler := func(w http.ResponseWriter, r *http.Request) {
        b, _ := ioutil.ReadAll(r.Body)
        defer r.Body.Close()
 
        var m map[string]interface{}
        _ = json.Unmarshal(b, &m)
 
        word := m["word"].(string)
        language := m["language"].(string)
 
        resp, err := suite.mockServerService.Translate(word, language)    #2
        if err != nil {
            http.Error(w, "error", 500)
        }
        if resp == "" {
            http.Error(w, "missing", 404)
        }
        w.Header().Set("Content-Type", "application/json")
        _, _ = io.WriteString(w, resp)
    }
    mux := http.NewServeMux()
    mux.HandleFunc("/", handler)
    suite.server = httptest.NewServer(mux)                                #3
}
 
func (suite *HelloClientSuite) TearDownSuite() {
    suite.server.Close()                                                  #4
}

See a handler that reads the request and delegates to a mock service. section 6.6

How Shipping Go: Develop, deliver, discuss, design, and go again covers it

How deep it goes
Builds a fake HTTP server and client tests, while deferring circuit breakers and other advanced system patterns.
Examples
Uses a Go translation client, an HTTP test server, and mocked success and error responses.
What it assumes
Assumes an API client and a defined contract for the external service.
What you can do after
Build unit tests for an API client using a fake HTTP server and controlled responses.
How it connects
Connects fakes with mocks, stubs, interfaces, and unit testing.

Also explained in

Compare how 6 books explain test double →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Shipping Go: Develop, deliver, discuss, design, and go again. The section links point to the parts of the book it draws on.