oslac

Search

Search IconIcon to open search

Fakes

Published: Jun 1, 2023
Updated: Jun 1, 2023

Summary

A fake doesn’t use a mock framework; instead fake is a lightweight implementation of an API that behaves like the real implementation, but isn’t suitable for production (e.g. an in-memory database). You shouldn’t need to write your own fakes often since fakes should usually be created and maintained by the owner of the real implementation1.

Example

Fakes can be used when you can’t use a real implementation in your test, e.g. if the real implementation is too slow or it talks over the network.

A fake might not exist for an object you need to use in a test. Writing one can be challenging because you need to ensure that it has similar behavior to the real implementation, now and in the future.

Fakes can be a powerful tool for testing

A single fake has the power to radically improve the testing experience of an API.

In a software organization where fakes are rare:

Finally, you could decide to settle on using a real implementation or resort to other test double techniques.

Writing Fakes

A fake requires more effort and more domain experience to create because it needs to behave similarly to the real implementation.

A fake also requires maintenance. Whenever the behavior of the real implementation changes, the fake must also be updated to match this behavior. Because of this, the team that owns the real implementation should write and maintain a fake.

If a team is considering writing a fake, a trade-off needs to be made on whether the productivity improvements that will result from the use of the fake outweigh the costs of writing and maintaining it. If there are only a handful of users, it might not be worth their time, whereas if there are hundreds of users, it can result in an obvious productivity improvement.

To reduce the number of fakes that need to be maintained, a fake should typically be created only at the root of the code that isn’t feasible for use in tests.

When a database can not be used in tests, a fake should exist for the database API itself rather than for each class that calls the database API.

Maintenance

Maintaining a fake can be burdensome if its implementation needs to be duplicated across programming languages, such as for a service that has client libraries that allow the service to be invoked from different languages.

Fidelity

A fake should maintain fidelity to the API contracts of the real implementation. The fake must have perfect fidelity to the real implementation, but only from the perspective of the test.

Summary

For any given input to an API, a fake should return the same output and perform the same state changes of its corresponding real implementation.

Examples

  • For a real implementation of database.save(itemId), if an item is successfully saved when its ID does not yet exist, but an error is produced when the ID already exists, the fake must conform to this same behavior.

  • A fake for a hashing API doesn’t need to guarantee that the hash value for a given input is exactly the same as the hash value that is generated by the real implementation, tests likely don’t care about the specific hash value, only that the hash value is unique for a given input. If the contract of the hashing API doesn’t make guarantees of what specific hash values will be returned, the fake is still conforming to the contract even if it doesn’t have perfect fidelity to the real implementation.

Testing Fakes


  1. Testing on the Toilet: Know Your Test Doubles ↩︎