A Fake Object offers limited functionality like the real object. But the implementation is lightweight. The fake object avoids the complications that make the real thing a difficult dependency.

Tackling the fake news problem

  • How can we verify logic independently when depended-on objects cannot be used?
  • How can we avoid Slow Tests?

When you can’t use a real implementation in your test (network/database). You should use a Fake Object whenever our SUT depends on other components that are unavailable or which make testing difficult or slow.

Some situations:

  • Fake Database
  • In-Memory Database
  • Fake Web Service
  • Fake Service Layer
  • Inject a Fake Object that implements async, but does so synchronously.

Fake Service 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)
  }
}

Define a Fake Object that conforms to the protocol. Inject it from test code, and use to write test cases.

struct FakeService: Service {
  func hello(_ name: String) -> String {
    return "Hello, \(name)"
  }
}
class GreetTests: XCTestCase {
  func test_greet_withWebService_shouldStartWithHello() {
    let sut = Greet(FakeService())

    let result = sut.greeting("Dummy")

    XCTAssertTrue(result.hasPrefix("Hello"))
  }
}