TDD – Implementing TDD with Repository Pattern

patterns-and-practicesrepositorytdd

In my new project, I decided to try with TDD. And in very beginning I encountered a problem. First thing that I want to do in my application is to give ability to read data from data source. For this purpose, I want to use repository pattern.
And now:

  • If test are for real implementation of repository interface, I will be testing class that has access to database, and I know that I should avoid that.
  • If test are for not real implementation of repository pattern, I Will be testing well… just mock. There will be no any piece of production code tested in those unit tests.

I'm thinking about this from two days and still cannot come up with any reasonable solution. What I should do?

Best Answer

What a repository does is translate from your domain onto your DAL framework, such as NHibernate or Doctrine, or your SQL-executing classes. This means that your repository will call methods on said framework to perform its duties: your repository constructs the queries needed to fetch the data. If you're not using an ORM-framework (I hope your are...), the repository would be the place where raw SQL-statements are built.

The most basic of these methods is the save: in most cases this will simply pass the object from the repository onto the unit of work (or the session).

public void Save(Car car)
{
    session.Save(car);
}

But let's look at another example, for example fetching a car by its ID. It might look like

public function GetCarWithId(String id)
{
    return Session.QueryOver<Car>()
                    .Where(x => x.Id == id)
                    .SingleOrDefault();
}

Still not too complex, but you can imagine with multiple conditions (get me all the cars made after 2010 for all brands in the 'Volkswagen' group) this gets tricky. So in true TDD fashion you need to test this. There are several ways to do this.

Option 1: Mock the calls made to the ORM framework

Sure, you can mock the Session-object and simply assert that the right calls are made. While this tests the repository, it is not really test-driven because you are just testing that the repository internally looks the way you want it to. The test basically says 'the code should look like this'. Still, it is a valid approach but it feels like this kind of test has very little value.

Option 2: (Re)build the database from the tests

Some DAL-frameworks give you the ability to build the complete structure of the database based on the mapping files you create to map the domain onto the tables. For these frameworks the way to test repositories is often to create the database with an in-memory database in the first step of the test and add objects using the DAL-framework to the in-memory database. After this, you can use the repository on the in-memory database to test if the methods work. These tests are slower, but very valid and drive your tests. It does require some cooperation from your DAL-framework.

Option 3: Test on an actual database

Another approach is to test on an actual database and isolate the unittest. You can do this in several ways: surround your tests with a transaction, clean up manually (would not recommend as very hard to maintain), completely rebuild the database after each step... Depending on the application you are building this may or may not be feasible. In my applications I can completely build a local development database from source control and my unittests on repositories use transactions to fully isolate the tests from each other (open transaction, insert data, test repository, rollback transaction). Every build first sets up the local development database and then performs transaction-isolated unittests for the repositories on that local development database. It's a little slower then a pure Unittest but the tests are extremely valuable and catch a lot of issues.

Don't test the DAL

If you are using a DAL framework such as NHibernate, avoid the need to test that framework. You could test your mapping files by saving, retrieving and then comparing a domain object to make sure everything is okay (be sure to disable any sort of caching) but it's not as required as a lot of other tests you should be writing. I tend to do this mostly for collections on parents with conditions on the children.

When testing the return of your repositories you could simply check to see if some identifying property on your domain object matches. This can be an id but in tests it's often more beneficial to check a human readable property. In the 'get me all the cars made after 2010....' this could simply check that five cars are returned and the license plates are 'insert list here'. Added benefit is that it forces you to think about sorting AND your test automatically forces the sorting. You'd be surprised how many applications either sort multiple times (return sorted from the database, sort before creating a view object and then sort the view object, all on the same property just in case) or implicitly assume the repository sorts and accidentally remove that somehwere along the way, breaking the UI.

'Unit test' is just a name

In my opinion, unit tests should mostly not hit the database. You build an application so that every piece of code that needs data from a source does this with a repository, and that repository is injected as a dependency. This allows for easy mocking and all the TDD-goodness you want. But in the end you want to make sure that your repositories perform their duties and if the easiest way to do that is hit a database, well, so be it. I've long let go of the notion that 'unit tests should not touch the database' and learned that there are very real reasons to do this. But only if you can do this automatically and repeatedly. And weather we call such a test a 'unit test' or an 'integration test' is moot.