3

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.)

  • That is an odd scenario. Is that what the code is doing you want to test? Otherwise it's unclear what specific behavior that scenario is meant to test. – Telastyn Jul 2 '15 at 13:58
  • The scenario is a data synchronisation - we're importing a list (of users in the real case) from one system which has been exported from another - so a stream of data gets posted across, turned into user objects and then updated or inserted. That's not really the point though, I seem to run into this any time a class has a DAO as a collaborator and the only side effects are "insert was called on DAO" - if I add a check that the DAO method was called, is that not testing the implementation of the class under test, or is "call insert on DAO" behaviour? – Chris Cooper Jul 2 '15 at 14:02
4

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.