Integration testing demonstrated (a data access testing with NHibernate)

 

In this post, I’ll talk about and demonstrate integration testing. If you are just starting out with integration testing, you want to test small before you test big. For instance, full-system tests are good, but if they fail, they don’t give much of a hint as to where the failure is. Smaller integration tests will help narrow the area where the failure lies.  After designing a vertical slice of the application with my team, I like to test-drive the code (that is micro-test the code) into existence.  Then, each scenario gets a covering integration test to prove that all the pieces fit together.

A few rules to live by

· An integration test must be isolated in setup and teardown. If it requires some data to be in a database, it must put it there. Environmental variables should not cause the test to fail randomly.

· It must also run fast. If it is slow, build time will suffer, and you will run fewer builds – leading to other problems.

· Integration tests should be order-independent. It should not matter the order you run them. They should all pass.

· Feel free to make up rules that objectively result in fewer defects.

Testing a repository class

Below, you’ll see an integration test for ConferenceRepository.cs.  This code is in CodeCampServer, so you have full access to the whole system if that helps you understand what’s going on.

    [Test]

    public void GetByKey()

    {

      Conference theConference = CreateConference("Frank", "some name");

      Conference conference2 = CreateConference("Frank2", "some name2");

      using (ISession session = getSession())

      {

        session.SaveOrUpdate(theConference);

        session.SaveOrUpdate(conference2);

        session.Flush();

      }

 

      IConferenceRepository repository = new ConferenceRepository(_sessionBuilder);

      Conference conferenceSaved = repository.GetConferenceByKey("Frank");

 

      Assert.That(conferenceSaved, Is.Not.Null);

      Assert.That(conferenceSaved, Is.EqualTo(theConference));

      Assert.That(theConference.Key, Is.EqualTo("Frank"));

      Assert.That(theConference.Name, Is.EqualTo("some name"));

    }

You’ll probably want to pull down the entire source tree using TortoiseSVN using the Subversion url:  https://codecampserver.googlecode.com/svn/trunk

Just for context, this test is testing an implementation of the following interface:

  public interface IConferenceRepository

  {

    Conference[] GetAllConferences();

    Conference GetConferenceByKey(string key);

    Conference GetFirstConferenceAfterDate(DateTime date);

    Conference GetMostRecentConference(DateTime date);

    Conference GetById(Guid id);

    void Save(Conference conference);

    bool ConferenceExists(string name, string key);

    bool ConferenceKeyAvailable(string key);

  }

Every test needs to set up its own state, so in this test, we see that the beginning of the test is using the application’s data access layer to save two Conference objects to the database.  CodeCampServer using NHibernate, so the test will use the same when setting up the database for the test.  If you examine the source, you will notice that the base class for all the NUnit test fixtures runs a command that clears out every table in the local database.  This is important because each test needs a known starting point, and the easiest starting point is an empty database.  Note that we’re talking about the local developer’s database, which should be created and updated by the local build.

After the database has two records, our class under test runs the GetConferenceByKey() method and returns a Conference.  Our assert statements can then verify the code did the right thing.  This test goes all the way through the data access layer and to the database.  If anything was awry along the way, the test would fail.

 

My hope is that this brief example will fill in some gaps that may exist in your understanding of integration testing.  Even though I’m doing integration testing and not unit testing, I’m keeping the scope of the test small because the larger the scope of the test, the harder it is to pinpoint the cause of any failure.

 

My feed:  http://feeds.jeffreypalermo.com/jeffreypalermo