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?
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.
1
-
Don’t test trivial or obvious repository methods.
If the methods are trivial CRUD operations, all you’re really testing is whether parameters are mapped correctly. If you have integration tests, such errors will become immediately apparent anyway.
This is the same principle that applies to trivial properties, like this one:
public property SomeProperty { get { return _someProperty; } set { _someProperty = value; } }
You don’t test it, because there’s nothing to test. There’s no validation or other logic in the property that needs to be verified.
-
If you still want to test those methods…
Mocks are the way to do it. Remember, these are Unit Tests. You don’t test the database with unit tests; that’s what Integration Tests are for.
More Information
The Full Stack, Part 3: Building a Repository using TDD (start watching at about 16 minutes in).
7
In Test Driven Development: By Example (Beck, Kent 2002) – Chapter 27. Testing Patterns, Kent explains how you can use mocks to test the Database interface. In short, you can have a mocked version of you database interface object, or ORM if you’re using one, and check if the queries (or ORM method calls) are the expected ones. For example:
function testCustomerLookup() {
var db = new MockDatabase();
db.expectQuery("SELECT name FROM customers WHERE id = 123");
db.returnResult("Customer 1");
var repo = new UserRepository(db);
expect(repo.getCustomerById(123)).toEqual("Customer 1");
}
One could argue that you should not test it because you’re more or less testing the implementation and just the argument translation from one layer to another. I am not convinced by this argument, since it is still code and you can mess with it. I think that this theses comes from the fact that it is hard to effectively test the Database interface and people don’t won’t to deal with it. But I think that the vast majority of your code should be tested, and that is no exception.