Wednesday, 17 December 2008

Fluent NHibernate - Integration Tests

In last two posts I have covered the Flunet NHibernate introduction and Coneventions together with AutoPersistenceModel. In this part I would like to show how Fluent NHibernate can speed up writing integration tests which should guarantee that mappings are correct. If there is a problem with mappings then for sure that is something you would like to want know about as fast as possible therefore some sort of automated tests are necessary.

Lets check what Fluent NHibernate has to offer:

1) SessionSource class which helps to deal with NHibernate configuration, session management and recreating database schema. Why is it useful? "Just" to check if mappings are correct you don't need to have any test data, you need only clear schema and database to connect with. You can use SQLite to create in-memory database. This is a neat and robust solution and moreover it eliminates external dependency -- we don't need any external database any more. Check the example:



   1:  [SetUp]
   2:  public void SetUp()
   3:  {
   4:      // create and configure persistance model including changes in Conventions
   5:      var persistenceModel = new PersistenceModel();
   6:      persistenceModel.Conventions.GetTableName =
   7:          type =>
   8:          String.Format("[{0}.{1}]", type.Namespace.Substring(type.Namespace.LastIndexOf('.') + 1), type.Name);
   9:      persistenceModel.Conventions.GetPrimaryKeyNameFromType = type => type.Name + "ID";
  10:      persistenceModel.Conventions.GetForeignKeyNameOfParent = type => type.Name + "ID";
  11:      persistenceModel.Conventions.GetForeignKeyName = prop => prop.Name + "ID";
  12:  
  13:      // add mappings
  14:      persistenceModel.addMappingsFromAssembly(typeof (Product).Assembly);
  15:  
  16:      // configure nhibernate using SQLite database
  17:      var config = new SQLiteConfiguration()
  18:          .InMemory()
  19:          .ShowSql();
  20:  
  21:      sessionSource = new SessionSource(config.ToProperties(), persistenceModel);
  22:  
  23:      // create NHibernate session
  24:      session = sessionSource.CreateSession();
  25:  
  26:      // recreate schema
  27:      sessionSource.BuildSchema(session);
  28:  }


Here is an implementation of BuildSchema(...) method:



   1:  public void BuildSchema(ISession session)
   2:  {
   3:      IDbConnection connection = session.Connection;
   4:  
   5:      string[] drops = _configuration.GenerateDropSchemaScript(_dialect);
   6:      executeScripts(drops, connection);
   7:  
   8:      string[] scripts = _configuration.GenerateSchemaCreationScript(_dialect);
   9:      executeScripts(scripts, connection);
  10:  }


As you can see, it uses NHibernate methods to generate DDL for dropping and creating tables for selected dialect (SQLite in this case). One thing worth noticing is that DDLs will be as accurate as mappings. So if you skip some information (like nullable fields) then don't expect to see it there!

2) Thanks to the SessionSource class we can query in-memory database and we have access to NHibernate Session ... it's time to check our mappings, we can use PersistenceSpecification class to do that:



   1:  [Test]
   2:  public void ProductReviewTest()
   3:  {
   4:      new PersistenceSpecification<ProductReview>(session)
   5:          .CheckProperty(x => x.Comments, "some nice comment")
   6:          .CheckProperty(x => x.EmailAddress, "test@test.com")
   7:          .CheckProperty(x => x.ModifiedDate, DateTime.Today)
   8:          .CheckProperty(x => x.Rating, 4)
   9:          .CheckProperty(x => x.ReviewDate, DateTime.Today)
  10:          .CheckProperty(x => x.ReviewDate, DateTime.Today)
  11:          .CheckProperty(x => x.ReviewerName, "test name")
  12:          .CheckReference(x => x.Product, CreateNewProduct())
  13:          .VerifyTheMappings();
  14:  }

Under the hood PersistenceSpecification class will save the object (ProductReview) to the database and then using another connection it will fetch the object back to make sure that all properties have correct values set. It's not a revolution but for sure it can save lots of time and thanks to neat and readable code it will increase maintainability.

Source Code

As always you can download the source code, and the whole sample web application. You will find there separate project for tests. I have created an AbstractTestBase class which is responsible for configuration and it also exposes NHibernate session. There are also tests for mappings, which use exposed NHibernate session and at least on my machine all tests pass ;)


(EDIT: Examples in this post have been updated on 8.02.2009 to reflect changes in Fluent NHibernate API)

Useful Links:
Other related posts:

2 comments:

Sosh said...

Can you 6-11 in the first section of code? (i.e. why do you need this convention code?)

Thanks,

S

Marek Blotny said...

Basically you want to have one set of mappings which you use in your application and the same set you want to reuse while writing the tests. That's why you need to apply the same conventions.

The code in this example is out-dated. API for conventions has changed significantly, you can learn more about conventions here: http://marekblotny.blogspot.com/2009/04/conventions-after-rewrite.html