views:

112

answers:

2

I've just encountered an error in my app that probably could have been caught with some integration tests, so I think its high time I wrote some!

My question relates to the setup of these tests, and at what layer of the code you run the tests against.

Setup

Considering I should have lots of integration tests, I don't want to be creating and dropping a test database for every test, this would be epically slow (even if its an SqlLite in-memory one). My thoughts are:

  1. Have a test db which sits next to my dev db
  2. Before testing, run some reset script which will set up my schema correctly and insert any necessary data (not test case specific)
  3. Simply use this test db as if its the real db.

However, it seems very wasteful that I have to run my Fluent NHib configuration in every [Setup]. Is this just tough? What are my options here?

My session is currently wrapped in a UoW pattern, with creation and destruction performed on begin_request and end_request (MVC web application) respectively. Should I modify this to play well with the tests to solve this problem?

Testing

When it comes to actually writing some tests, how should I do it?

Should I test from the highest level possible (my MVC controller actions) or from the lowest (repositories).

If I test at the lowest I'll have to manually hard code all the data. This will make my test brittle to changes in the code, and also not representative of what will really happen in the code at runtime. If I test at the highest I have to run all my IoCC setup so that dependencies get injected and the whole thing functions (again, repeating this in every [SetUp]?)

Meh! I'm lost, someone point me in the right direction!

Thanks

+1  A: 

In regards to creating the session factory, I create a class called _AssemblyCommon in my test project and expose the session factory as a static from there. A method marked with the [SetupFixture] (NUnit) attribute configures the session factory.

In general integration tests should cover CRUD operations in the repositories. To do this, I have one test method per object (or aggregate root) and do an insert, retrieve, update, and delete all within that method. I also test any cascade deletes that I've defined. Keeping these operations in a single method doesn't leave any traces behind in the database. I do have some integration tests that leave behind test data but that hasn't been a problem.

Your higher level operations should be unit tested and mock (I use Moq) the repositories if possible.

Jamie Ide
+1  A: 

In my current MVC app I've found that it's enough to test the interaction between the repositories and the database. More than anything, this is to iron out any wrinkles in the NHibernate mapping. Everything (ok I'm exaggerating when I say everything) above that layer is unit tested in isolation. I did have some integration tests from the controllers all the way down the stack to the database and these used an IoC container (StructureMap) to build the controllers and the dependencies, but I found these tests were really not adding anything and they were quite an overhead to maintain, so I've removed them from the 'integration' tests for now - I may find a reason to put them in but so far I haven't.

Anyway, the test process I use works like this:

The build process for the data access layer test assembly creates the test database via a FluentNHibernate configuration ExposeSchema() call. The build process then runs some NHibernate repository level code to populate reference tables in the database.

Each integration test that runs is then wrapped in a System.Transactions.TransactionScope using() statement and the TransactionScope never has Complete() called, so each test can run in isolation and the results can be setup and verified within the using() scope by an ISession without altering the state of any other test data. e.g.

  using (new TransactionScope())
  {
    var session = NHibernateTestSessionCreator.GetNHibernateSessionForIntegrationTesting();
    // This is an NHibernate ISession - setup any dependencies for the repository tests here

    var testRepository = new NHibernateGenericRepository<NewsItem>(NHibernateTestSessionCreator.GetNHibernateSessionForIntegrationTesting())
    // repository test code goes here - the repository is using a different ISession

    // Validate the results here directly with the ISession
  }

  // at this point the transaction is rolled back and we haven't changed the test data

This means that I don't have to modify the UnitOfWork implementation I'm using - the transaction is rolled back at a higher level.

Steve Willcock