We replace a real object with a test-specific object that feeds the desired indirect inputs into the system under test.

xUnit Patterns.com

  • How can we verify logic independently when it depends on indirect inputs from other software components?

When we need defined return values from a method. In a test, it is often useful to have fixed hard-coded return value for a method that the SUT calls. The test then asserts that the SUT reacts in an expected way to the defined return value.

Stubs may also record information about calls, such as an email gateway stub that remembers the messages it ‘sent’, or maybe only how many messages it ‘sent’.

How it works

  1. Define a test-specific implementation of an interface on which the SUT depends.
  2. Install the Stub so that the SUT uses it instead of the real implementation.
  3. Called by the SUT during test execution, the Stub returns the previously defined values. The test can then verify the expected outcome.

Note: Test-driven development often causes us to create Temporary Test Stubs as we write code from the outside inwards; these shells evolve into the real classes as we add code to them. We really should have at least one test that verifies it works without a Test Stub.

Motivating Example

protocol Service {
  func hello(_ name: String) -> String
}

class Greet {
  let service: Service

  init(_ service: Service) { self.service = service }

  func greeting(_ name: String) -> String { return self.service.hello(name) }
}
class StubService: Service {
  var greeting: String?

  init(with: greeting: String) { self.greeting = greeting }

  // Service interface
  func hello(_ name: String) -> String { return greeting ?? "" }
}

class StubTests: XCTestCase {
  func test_greeting_withService_shouldReturnFoo() {
    let sut = Greet(StubService(with: "foo"))

    let resut = sut.greeting("dummy")

    XCTAssertEqual(resut, "foo")
  }
}