views:

246

answers:

2

Consider a beginner dealing with Dependency Injection. We're analyzing two relevant classes in NerdDinner.

DinnerRepository from the application: Repo image

FakeDinnerRepository from the tests: Fakes image

They implement different logic, which of course is necessary, as the key idea here is to implement the IDinnerRepository, and provide different implementations and private members.

I understand the test is for the controller, but I am concerned that there are two different implementations of the data access logic. Consider any project that uses any kind of ORM, ADO.NET, SubSonic, or whatever flavour of data access you like. Yes, you can setup your fake repository to match the real repo.

My worry is that over time, implementation details in the real repo change. Perhaps a typo slips in, or some other important implementation detail changes in the query. This leads to a potential mismatch of the logic in the Model between the fake and the real repo. The worry is that the implementation of the real repo and test repo get out of sync.

Questions:

  • How would you test the Model in this case?
  • Is it appropriate to test the model?
  • Is it a matter of discipline to ensure your test keep up with the implementation of the business logic?
+4  A: 

This is probably not a complete answer to your question, but I'll try to get part of the way there.

The interface - in this case IDinnerRepository - should be seen as a contract. That means that any implementation has to fulfill this contract. If the method is FindAllDinners(), then that is basically what it should do. A fake repository used for unit tests can usually be a LOT simpler than the real thing (using a Dictionary for instance), so keeping up with the real implementation really shouldn't be seen as a problem, rather see it as a requirement.

The reason for the fake repository to exist in the first place is testing. Basically, everything that can be tested should be tested. And taking the database out of the equation is the point of an in-memory fake repository. The data access is not the point of the test, so we replace it. The fake repository is much faster to set up and use, and we can easily make sure the repo is in a state that it needs to be for the code under test to pass.

So what you do is to pass the model a copy of your fake repository in your unit tests, and make sure that whatever happens in the model code is reflected in the fake repository.

I think you'll find that in practice, it won't be a problem to keep the repositories in sync. If requirements change, you'll change the interface and both implementations will need to change. If the intentions change, you'll probably get to a point where your unit tests start to break.

Hope this helps!

Rune Jacobsen
Thanks Rune. That makes sense. I guess the base of the question is: should you test your data access? Sounds like you're saying 'no'.
p.campbell
That is actually one of those things I struggle with myself as a beginning TDDer. The way I currently understand it, your unit tests should leave the actual data access alone, while it is apparently a good idea to have integration tests that also check that the actual data access works fine. These integration tests will be slower, but that should be acceptable since they probably won't be run as often as the "pure" unit tests.
Rune Jacobsen
+2  A: 

It's worth nothing that FakeDinnerRepository is to test code that requires an IDinnerRepository. Not to test DinnerRepository itself. If we used a real dinner repository to test things like the DinnerController we would not only be testing DinnerController's functionality, but also the data access itself. While the data access bits should certainly be tested, they should not be tested in the dinner controller's tests.

Testing your data access is a rather controversial topic. I believe the reason most people leave it out of their unit tests is simply that it takes much longer to execute. Having to query the database will add a ton of time to your testing that most developers would rather not endure.

Whether your data access gets tested in unit tests or more formal functional testing is up to you. But it certainly needs to be tested. Personally, I'm a fan of having transactional unit tests for the data access layer. Just make sure you're only testing your queries, and not testing that your ORM is working as it's supposed to (that's the ORMs job).

jcm