liveBook · Manning

Mocking

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

A mock is a programmable test double that records or verifies interactions and can return controlled results or errors.

A service that calls another service can be hard to test reliably: the dependency may be unavailable, return an unexpected value, or receive the wrong input. A mock stands in for that dependency. Like a stub, it supplies controlled results; unlike a simple stub, it can also verify which methods were called and with what arguments. section 6.5 section 6.4

Put an interface between the code under test and its dependency, then inject a mock implementation. For a remote translation service, the HelloClient interface exposes Translate(word, language), allowing tests to replace the external client without testing the external service itself. section 6.5

A practical test sequence is:

  • Create a fresh mock and service for each test. A Testify suite can use SetupTest for this shared setup. section 6.5.1
  • Set an expected call and its return values with On(...).Return(...), run the service, check the result, and call AssertExpectations. section 6.5.2
  • Vary the expectations to exercise input normalization, errors, and repeated calls. An expectation of one client call can test whether a cache serves the second request. section 6.5.2

Catch a wrong argument

Expect Translate("foo", "bar") to return "baz", then call the service with "Foo". The mock reports that the expected call did not occur. Lowercasing the input before the client call makes the expectation pass. A separate test can return an error from the mock and verify that the service returns an empty string. section 6.5.2

Listing 6.14 remote_translator_test.go
package translation_test
...
 
func (suite *RemoteServiceTestSuite) TestTranslate() {
    // Arrange
    suite.client.On("Translate", "foo", "bar").Return("baz", nil)   #1
 
    // Act
    res := suite.underTest.Translate("foo", "bar")
 
    // Assert
    suite.Equal(res, "baz")                                         #2
    suite.client.AssertExpectations(suite.T())                      #3
}

A compact Go example of arranging a client response, calling the service, and asserting both the result and the expected interaction. section 6.5.2

Use mocks when the interaction matters, not as a replacement for sound design. They require more setup than stubs; if a service needs a tangle of mocks, consider whether the service or its dependencies should be simplified. section 6.7 section 6.8

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

How deep it goes
Works through suite setup, call expectations, argument failures, error injection, and cache verification; does not explore mock-library internals.
Examples
Tests a Go translation client with expected arguments, a case mismatch, an injected error, and a single-call cache expectation.
What it assumes
Assumes familiarity with Go tests and services; establishes the client interface and mock suite before using expectations.
What you can do after
Apply it via Testify to verify dependency calls and exercise controlled results and errors.
How it connects
Connects interfaces and dependency injection to stubs, fakes, test suites, unit tests, and caching.

Also explained in

Compare how 21 books explain mocking →

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.