Broadly speaking, these tools are used to make things easier when testing by setting up objects/data in a reproducible and reliable manner with minimal effort. They might take a minute to set up, but as soon as the data is needed in more than two tests youâll be thankful you took the time.
Fixture
A test fixture is an environment, a state or a dataset we use to consistently test a piece of software.
This example used by Martin Fowler seems appropriate:
When you write tests in a reasonably sized system, you find you have to create a lot of example data. If I want to test a sick pay calculation on an employee, I need an employee. But this isnât just a simple object - Iâll need the employeeâs marital status, number of dependents, some employment and payroll history. Potentially this can be a lot of objects to create.
All of that data is what we call a test Fixture, no matter where it is or what shape it has as long as itâs valid. It could be something as simple as a json
file with all the data we need.
Often enough, the term Fixture is used to also refer to the utilities we build to provide that data. Example.
For example, say you need the data from the example above to be persisted in your testing database to see whether a given function can fetch it correctly. You might create an EmployeeFixture
with a save()
function that receives an Employee
and persists it.
Here the naming gets kinda muddy: Although we usually call these types of helpers Fixtures, what they actually do is provide the Fixture itself, they set up the testing environment.
Builder
A creational design pattern that lets you easily construct complex objects step by step as needed. The pattern allows you to produce different types and representations of an object using the same construction code.
They still end up producing a (in memory) Fixture. Itâs just a more useful and flexible way of getting it.
Example
Say you want to test how your application behaves when saving a user to the database if it has a faulty email address. You could go new User(name, age, id, email, maritalStatus, ...)
, but you really only care about the email for this test case. Plus, imagine name
, age
and maritalStatus
all go through validations, so you canât just put whatever in those fields. It would be nice if you could use a sort of âdefault valid Userâ and just set a faulty email to it.
Something like User myUser = new UserBuilder().withEmail('doesntWork').build()
with the Builder setting the rest of the properties to some irrelevant (but valid) default for you. Example.
Details
Youâll often want to test behavior affecting semi complex entities.
You can use the Builder pattern to your advantage by having it set some sane defaults to, in our example, the User while also allowing you to customize the Entity at will.
This also gives you a centralized standard âUser makerâ for your tests. So as long as this Builder accurately reflects the behavior of the production entity, you can be sure that your tests are relevant.
Plus, if something changes about your User (for example, the age now defaults to 18 if not set) you only need to apply the change in the Builder instead of parsing all the tests that use the User entity.
Object Mother
An Object Mother is a sort of fancy factory pattern, delivering prefabricated test-ready objects via a simple method call.
Again Mr. Fowler:
[âŚ] it makes sense to have a factory object that can return standard Entities. Maybe âJohnâ, an employee who just got hired last week; âHeatherâ and employee whoâs been around for a decade. Object Mother is just a catchy name for such a factory
Example
You might find yourself using our UserBuilder
in a few different tests just to end up creating the same type of User. What âtype of Userâ means depends on context but think of your typical Admin User, Guest User, New User, etc.
You can remove this duplication by abstracting the User creation into an Object Mother and just write testAdminUser = new UserMother.withAdminRole()
or testAdminUser = new UserMother.admin()
and call it a day! Example.
Details
Object Mothers differ from Builders in that Builders usually create dummy versions of domain Entities with no specific scenario in mind while Object Mothers are meant to Build more specific and complex instantiations of your domain Entities with the necessary data.
As soon as you find yourself creating the same kind of user in two different tests, go for an Object Mother.
Youâll use it to reduce code duplication, increase test maintainability and encourage other developers to write more tests by making test objects super-easily accessible.