Unit-testing – Unit testing implementation vs behaviour

integration-teststddunit testing

Is it possible to write a unit test (as opposed to an integration test) in the following scenario:

  • I have a list of Foo objects, some of which already exist in the database, some don't
  • I want to iterate over the list, update the ones I already have in the database and add new ones

So, I write a test which has a FooImporter with an importFoos(List<Foo> foos) method. And then I need to mock out my DAO layer, so I mock the FooDAO which gets injected into the FooImporter…and then I'm feeling like I'm testing the implementation not the behaviour! In my test I end up with a bunch of expectations on calls (e.g. I put in an "existing" Foo record, then I mock the behaviour of the FooDAO to return true on the check for duplicate, and then add an expectation for the insert behaviour etc.)

So can I actually unit test this close to the data layer effectively, or should I be integration testing? And if so, can this not be done sensibly in a TDD manner?

(Before this gets flagged as a duplicate, this question is actually about the talk which inspired this question but the answer doesn't really convince me that this solves the coupling of tests to implementation behaviour.)

Best Answer

A unit test should test the job that the tested unit has, no more, no less.

If your method has the job of acessing the database and issuing UPDATE and INSERT commands as required, then that is what you have to test. When your collaborator is a data base, then issuing SQL is not an implementation detail: it is the behaviour of that unit.

Compared to the huge amount of code that you leverage by using a real database with its million LOC, that might seem small, but that impression is misleading.

A unit test should be small. Small tests are easy to get right. Small tests go well with small methods, and small methods are also easy to get right. Trying to do two things at the same time makes your code more than twice as large, and therefore isn't a good trade-off.