views:

238

answers:

5

I was wondering what approaches others might have for testing domain services against a database? I already have a series of mock repositories that I am able to use in the domain services for testing the domain services themselves. Part of the construction of these mock repositories is that they build out sample aggregates and associated entities and validate them against the same business rules that would otherwise be used within the model. This also provides a nice and simple means to detect potential impact points within the entities themselves, in the event that their interfaces change.

The main problem that I see with live testing of my SQL-backed repositories is database consistency. For example, once a test is run the "create" aspects have already been run. Running them again would obviously cause failures, as the database is no longer pristine. I was considering create a mirrored database used just for this type of testing. It would be minimal, containing structure, programmability, constraints, etc. I would also provide a minimal set of data for certain established tests. My line of thinking is that I could have a stored procedure that I could call to reset the database to the "pristine" state with base data before the start of the test run.

While this is not as important on a developer machine after the functionality has been initially verified, I am looking more into the importance of running these tests as part of the nightly build; so that in the event of a test failure, the build could be held back as to not foul the target deployment environment (specifically in this case, it would be the environment that the testing team uses).

I do not necessarily think that the platform matters, but in case anyone has implementation specific concerns, my environment looks like the following:

Windows 7 (Development) / Windows Server 2008 R2 (Server) Visual Studio 2008 Team Edition (C#) Microsoft SQL Server 2008 Standard (Development/Server)

I am using Team Build to run my builds, but that is most likely not a factor in the scope of the question.

+2  A: 

For example, once a test is run the "create" aspects have already been run. Running them again would obviously cause failures, as the database is no longer pristine.

Maybe you could make your unit tests transactional. Run your tests, roll them back, and the database is unchanged.

Spring has transactional unit test classes that make this easy to do. You just need a transaction manager.

duffymo
That is a great idea. My repositories already have a "Unit of Work" pattern implementation that I am using to create a TransactionScope() automatically. I didn't think to add a Rollback() method to go with my Commit(), but this might just be the easiest approach in this case.
joseph.ferris
This works very well. Thanks again! Much easier than the other approach I was considering!
joseph.ferris
+1  A: 

You can use SQL Server Express (I've done it with 2005, but haven't tried with 2008) to set up "test deck" databases that are stored as files. These can be checked in to source control, then test helper classes can (a) copy them to temporary folders, (b) write-enable them, and (c) connect to them. To restore the original state, delete the temporary copy and repeat a-c.

This is a huge pain. If you can get by with transactions (as duffymo suggested) I'd go with that. The pain points with transactions are nested transactions and distributed ones - watch out for those in your code.

TrueWill
You are right, it definitely looks to be a pain! As I mentioned in my comment to duffymo's answer, I already have automatic TransactionScope() generation in my repositories through a Unit of Work implementation. I will likely try the transaction approach, since I really am just looking to see if there is an underlying failure in the implementation, and not chaining the tests together (i.e. not looking to create "A", update "A", then delete "A" as individual tests.)
joseph.ferris
+1  A: 

You can create a bunch of data factories in testing code which initially run on startup of your test run. Then use the transaction rollback method to keep it pristine. To make it easier, subclass all your test classes and put the transaction accessor and rollback code in there. Rollback code can be set to automatically run at the completion of every test method.

tony.chevis
A: 

If your code is fairly database independent, using an in-memory database such as SQLite for unit testing (not integration testing) will give you the benefits of speed and ease (your test setups initialize the db).

Martin R-L
A: 

if you are actually executing unit tests for your repository that are hitting a database, YOU ARE NOT DOING UNIT TESTING. It might be a helpful test but it's not a unit test. That's an integration test. If you want to do that and call it a integration test then that is perfectly fine. However, if you are following good design principles in your repositories then you do not need to test the database, EVER, in your unit tests.

Quite simply, your repository unit test is NOT to test what wide effects occur in the database based on the input to the repository; it is to confirm that the input to the repository results in a call to a collaborator with such and such a set of values.

You see, the repository, like the rest of your code, should follow the Single Reposibility Principle. Basically your respoitory has ONE and ONLY ONE reposibility and that is to mediate domain model API concerns to the underlying data access technoloy layer (usually ADO.Net but could be Entity Framework or L2S or whatever). Going with the example of ADO.Net calls, your repository shouldn't take on the responsibilty of being a factory for the data layer and instead should take a dependency on a collaborator from the ADO.Net data interfaces (specifically IDbConnection/IDbCommand/IDbParameter etc). Simply take an IDbConnection as a constructor parameter and call it a day. This means that you can write repository unit tests against the interfaces and supply mocks (or fakes or stubs or whatever you need) and confirm that the required methods, in order, with the expected inputs are made. Go check out my MS blog on this exact topic -> http://blogs.msdn.com/b/schlepticons/archive/2010/07/20/unit-testing-repositories-a-rebuttal.aspx

Hopfully this helps you from making a mistake in your test and design in the future.

BTW: If you want to unit test the database you can. Just use Visual Studio Database Tests. Its built INTO vs and has been around since VS2005. This is nothing new. But I need to caution you, they need to be completely SEPERATE unit tests.

Jimmy Zimmerman