Usually I’m testing DAOs for CRUD operations. Using NHibernate, as persistent layer, what we are testing, in practice, is the mapping and all behavior we have defined in it (cascade, associations, types, user-types, custom-collections and so on).
For CRUD operation I mean single entity instance CRUD without test complex queries (to test complex-queries I’m using a different test suite with a Well-Known-Data-Base).
Testing NHibernate DAO implementation
The first step is the test of the generic/base implementation of the DAO. To test it I’m mocking NHibernate.
[Test]
public void Get_call_Get()
{
// Arrange
var sessionFactory = new Mock<ISessionFactory>();
var session = new Mock<ISession>();
sessionFactory.Setup(x => x.GetCurrentSession()).Returns(session.Object);
// Act
var dao = new Dao<FakeEntity, int>(sessionFactory.Object);
dao.Get(123);
// Assert
session.Verify(x => x.Get<FakeEntity>(It.Is<int>(id => id == 123)));
}
The CRUD test
A simple integration CRUD test may look like this:
[TestFixture]
public class MagazineTest
{
[Test]
public void MinimalCrud()
{
const int poid = 1;
var m = new Magazine(poid) {Title = "some title"};
var dao = new EntityDao<Magazine>(ServiceLocator.Current.GetInstance<ISessionFactory>());
using (new PersistenceRequest())
{
dao.MakePersistent(m);
ActionAssert.NotThrow(()=> dao.MakePersistent(m));
}
using (new PersistenceRequest())
{
var e = dao.Get(poid);
e.Should().Not.Be.Null();
e.Title.Should().Be.EqualTo("some title");
}
using (new PersistenceRequest())
{
var e = dao.Get(poid);
e.Title = "different title";
}
// should update through NHibernate's UoW
using (new PersistenceRequest())
{
var e = dao.Get(poid);
e.Title.Should().Be.EqualTo("different title");
}
// Delete
using (new PersistenceRequest())
{
var e = dao.Get(poid);
dao.MakeTransient(e);
}
using (new PersistenceRequest())
{
var e = dao.Get(poid);
e.Should().Be.Null();
}
}
}
The PersistenceRequest is an utility class I’m using to simulate the behaviour of the IHttpModule implementing the session-per-request pattern.
Note, the test-fixture does not inherit from a base class and does not have a any SetUp nor TearDown but indeed it need, at least, the configuration of the ServiceLocator, the configuration of NHibernate, the creation of schema, and so on.
For the whole CRUD-test-suite I want execute the configuration of the ServiceLocator, the configuration of NHibernate and its SessionFactory, just only one time and not before execute each test-fixture.
The SetUpFixture Attribute
Since few versions NUnit has a very useful feature: the SetUpFixture Attribute.
To have only one initialization, for all my CRUD-tests, I’m putting all test-fixtures starting from a specific namespace (in the image the namespace is “Cruds”) where I have the implementation of the class, marked with the [SetUpFixture], responsible to create the context for all CRUD tests.
The implementation of the CRUD context look like:
[SetUpFixture]
public class CrudTestContext
{
private WindsorContainer container;
[SetUp]
public void Configure_NHibernate_and_ServiceLocator()
{
container = new WindsorContainer();
container.Register(Component.For<IServiceLocator>()
.Instance(new WindsorServiceLocator(container)));
ServiceLocator.SetLocatorProvider(() => container.Resolve<IServiceLocator>());
container.Register(Component.For<ISessionWrapper>()
.ImplementedBy<FakeSessionWrapper>());
container.Register(Component.For<IConfigurationProvider>()
.ImplementedBy<NhTestConfigurationProvider>());
container.Register(Component.For<ISessionFactoryProvider>()
.ImplementedBy<SessionFactoryProvider>());
container.Register(Component.For<ISessionFactory>()
.Instance(container.GetService<ISessionFactoryProvider>().GetFactory(null)));
}
[TearDown]
public void Close_NHibernate()
{
container.Dispose();
}
}
public class NhTestConfigurationProvider : DefaultSessionFactoryConfigurationProvider
{
public NhTestConfigurationProvider()
{
AfterConfigure += ValidateOrCreateSchema;
}
private void ValidateOrCreateSchema(object sender, ConfigurationEventArgs e)
{
try
{
new SchemaValidator(e.Configuration).Validate();
}
catch (HibernateException)
{
var export = new SchemaExport(e.Configuration);
export.Drop(false, true);
export.Create(false, true);
}
}
}
If you want use the [SetUpFixture] you must use a NUnit2.5 compatible test-runner (ReSharper 4.5 does not support it).
UPDATE : I'm sorry, ReSharper 4.5 does SUPPORT it.
Hm, would that be the one feature that NUnit took from MSTest?
ReplyDelete@F Quednau
ReplyDeleteI don't know and I don't care ;)
I usually implement test context using inheritance. It seems semantically logical.
ReplyDeleteSo I have a structure like this for a set of test with the same context:
- Feature
--| Action1.cs (inherits from Context.cs)
--| Action2.cs (inherits from Context.cs)
using the SetUpFixtureAttribute (I believe it is available since NUnit 2.4) it will look pretty much the same except there is no explicit definition for context per ActionX.cs. So we have to know the assumptions in this case. Not really sure if I like this, but in any case it's a nice feature of NUnit.
What's the point of not using an static constructor (in the base class) and keeping the sf in a static field, for instance?
ReplyDelete@José
ReplyDeleteYou can inherit from only one class and I know some FX where there are 3 levels before the real test with override, override and override. From now on I'll use SetUpFixtureAttribute instead a base class per suite.
Hey Fabio, which IoC fwk are you using there?
ReplyDeleteIs "WindsorContainer" not enough to know which is?
ReplyDelete;)