Test Doubles or Test Data Builders?

One of my readers asked a question whether he should rather use test doubles (mocks) or Test Data Builders to create objects (collaborators) for testing purposes. I've decided to write down some thoughts in form of this blog post. I hope it makes things more clear.

The 'arrange' (or 'given') part of every test is where the objects are created.

In unit tests this "creation" usually means that we:

  • a) create test doubles of SUT's collaborators (SUT - System Under Test, or more informally this "something" that you want to test)
  • create the SUT itself (and usually inject collaborators)

In the case of integration and end-to-end tests it is very common that we create some real objects. This is because we are interested in how classes (modules, layers) cooperate with each other. Thus we rarely replace real classes (modules, layers) with some replacements (test doubles - mocks, stubs, fakes etc.). We want to see our stuff cooperating for real.

When I think about the tests I have recently written, I can say that in majority for cases, I use test doubles for unit tests, and Test Data Builders are for integration and e2e tests.

However, it happens that I create more complicated graphs of objects in unit tests using Test Data Builders. Rarely, but happens (my apologies, I can't recall any example right now). But this only happens when the objects are really, really "close" to each other. In other cases I use test doubles.

It happens even more frequently that I use test doubles for integration or e2e tests. This is rather obvious. Sometimes you need to ask some component to respond in a way which is hard to get when using a real object. I rarely (if ever) mock (meaning: in order to verify interactions) but rather stub (meaning: I expect objects to return some canned value). It also happens that I use fakes - e.g. simple maps instead of sophisticated caches.

One more comment. Test doubles can be used for just any collaborators, while Test Data Builders makes sense only for POJOs/beans and not for services. I think this is obvious, but decided to put this comment just in case.

I think that's all I have to say about this right now. Cheers!

Please comment using