Technology with opinion

Monday, June 15, 2009

NHibernate 101 Materials

I had the opportunity to speak at CINNUG's ORM Firestarter and present the topic on NHibernate 101.  The presentation is an overview of the NHibernate framework.  The sample app forward engineers a database based on the Hibernate mapping files.  There is also a sample web app which uses Open Session in View (OSIV) using NHibernate Session Scopes.



Tuesday, June 09, 2009

Unit Testing NHIbernate with SessionScopes

Ayende has a great article on Unit Testing NHibernate. However if are you using NHibernate Session Scopes you need to adapt it. I wanted to keep my tests simple, I want to test my DAOs and inject into them an ISessionFactory since this is where it's getting it's Session from. SessionScopes are a nice way in NHibernate not have to worry about managing your sessions, you want to control the behavior. You may end up doing this with AOP or in your web request however that's not pertinent to my test since the design of my DAOs have the Session Factory Injected.

We are reusing DAOs within Web code to integration code and the session management behavior is different in each one. I am also using the Spring.Net however this should work anywhere that you are injecting ISessionFactory and you are using SessionFactory.GetCurrentSession() to get your session.

Basically we're using the same code as in his example except we are using Rhino Mock to actually mock the SessionFactory. In addition we are exposing the SessionFactory so that you can inject it into your DAOs. We are still exposing the Session for use in your test fixture.

    public class InMemoryDatabaseTest : IDisposable
{
private static Configuration Configuration;
private MockRepository _mocks;
private static ISessionFactory _realSessionFactory;
private ISession _session;
private ISessionFactory _sessionFactory;

protected ISession Session
{
get { return _sessionFactory.GetCurrentSession(); } // simulates how DAOs are getting their Sessions
}

///
/// Gets the SessionFactory
///
public ISessionFactory SessionFactory
{
get { return _sessionFactory; }
}

public InMemoryDatabaseTest(Assembly assemblyContainingMapping)
{
_mocks = new MockRepository();

if (Configuration == null)
{
Configuration = new Configuration()
.SetProperty(NHibernate.Cfg.Environment.ReleaseConnections, "on_close")
.SetProperty(NHibernate.Cfg.Environment.Dialect, typeof(SQLiteDialect).AssemblyQualifiedName)
.SetProperty(NHibernate.Cfg.Environment.ConnectionDriver, typeof(SQLite20Driver).AssemblyQualifiedName)
.SetProperty(NHibernate.Cfg.Environment.ConnectionString, "data source=:memory:")
.SetProperty(NHibernate.Cfg.Environment.ProxyFactoryFactoryClass, typeof(DefaultProxyFactoryFactory).AssemblyQualifiedName)
.AddAssembly(assemblyContainingMapping);
_realSessionFactory = Configuration.BuildSessionFactory();
}

// Here we are mocking the Session Factory because we are using Session Scopes.
// However we want to return the same session every time
_sessionFactory = _mocks.CreateMock<ISessionFactory>();
_session = _realSessionFactory.OpenSession();
Expect.Call(_sessionFactory.GetCurrentSession()).IgnoreArguments().Return(_session).Repeat.Any();
_mocks.ReplayAll();

new SchemaExport(Configuration).Execute(true, true, false, true, _session.Connection, Console.Out);
}

public void Dispose()
{
Session.Dispose();
}
}
The Mock is the glue that makes this work so that you can adhere to the restrictions of your DAO implementation. Then you can execute the test much like Ayende does in his own, regardless of how your perform transaction management in your actual application.

    [TestFixture]
public class FelineDao_Tests : InMemoryDatabaseTest
{
public FelineDao_Tests() : base(typeof(Feline).Assembly) { }

[TestFixtureSetUp]
public void Initialize()
{
// Insert Test data
using (ITransaction tx = Session.BeginTransaction())
{
Session.Save(GetSnowLeopard());
Session.Save(GetDomesticCat());
tx.Commit();
}

Session.Clear();
}

[Test]
public void FindAll_Retrieval()
{
using (ITransaction tx = Session.BeginTransaction())
{
Dao dao = new Dao();
dao.SessionFactory = SessionFactory;
IList list = dao.FindAll();
Assert.AreEqual(2, list.Count);
tx.Commit();
}
}

[Test]
public void FindById_Retrieval_InValid_Id()
{
using (ITransaction tx = Session.BeginTransaction())
{
Dao dao = new Dao();
dao.SessionFactory = SessionFactory;
Feline o = dao.FindById(3);
Assert.IsNull(o);
tx.Commit();
}
}

[Test]
public void FindById_Retrieval_Valid_Id()
{
using (ITransaction tx = Session.BeginTransaction())
{
Dao dao = new Dao();
dao.SessionFactory = SessionFactory;
Feline o = dao.FindById(1);
Assert.IsNotNull(o);
tx.Commit();
}
}

private Feline GetSnowLeopard()
{
Feline feline = new Feline();
feline.Name = "Snow Leopard";
feline.Length = 74;
return feline;
}

private Feline GetDomesticCat()
{
Feline feline = new Feline();
feline.Name = "Domestic Cat";
feline.Length = 24;
return feline;
}
}

Props to Ayende for the original implementation of this and for his work on Rhino Mocks.