NHibernate/DAO Unit testing

Unit Testing with using NHibernate and Data Access Objects

    [TestClass]
    public class AccountTest
    {
        private DATestHelper<CustomerDAO, CustomerDE> dh = 
                                new DATestHelper<CustomerDAO, CustomerDE>();
        private DATestHelper<ProductDAO, ProductDE> dhProduct = 
                                new DATestHelper<ProductDAO, ProductDE>();
        
        [TestInitialize()]
        public void TestInitialize()
        {
            dh.Init();

            dhProduct.InitJoinSession(dh.session);
        }

        [TestCleanup()]
        public void TestCleanup()
        {
            dh.Cleanup();
            dhProduct.CleanupJoinedSession();
        }

        [TestMethod]
        public void GetAccountById()
        {
            CustomerDE c = dh.dao.FindById(1231);
            Assert.IsNotNull(c);
        }

        [TestMethod]
        public void GetProductByCode()
        {
            Assert.IsNotNull(dhProduct.dao.GetProductByProductCode("CODE_123"));
        }

    }

Helper class to mock NHibernate for integration testing:

public class NHibernateHelperMock
{
	public static readonly ISessionFactory SessionFactory;
	static NHibernateHelperMock()
	{
		try
		{
			string assemblyString = "No Library";
			string connstr = "Some error in config";

			string path = Directory.GetCurrentDirectory();

			System.Configuration.Configuration rootWebConfig =
			  ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

			System.Configuration.ConnectionStringSettings connString;
			if (0 < rootWebConfig.ConnectionStrings.ConnectionStrings.Count)
			{
				connString = rootWebConfig.ConnectionStrings
                                                          .ConnectionStrings["NHibernateDB"];
				if (null != connString)
					connstr = connString.ConnectionString;
			}

			assemblyString = System.Configuration.ConfigurationManager
                                                             .AppSettings["NHibernate.Mappings"];

			string[] assemblyStrings = assemblyString.Split(';');

			List asmemblies = new List();
			foreach (var item in assemblyStrings)
			{
				Assembly assembly = Assembly.Load(item);
				asmemblies.Add(assembly);
			}

			FluentConfiguration fc = Fluently.Configure()
			.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration
                                .MsSql2008.Raw("connection.isolation", "ReadUncommitted")
				.ConnectionString(connstr)
				.ShowSql()
					)
			 .ExposeConfiguration(c =>
				c.SetProperty("current_session_context_class", "thread_static"))
			.Cache(c => c.Not.UseQueryCache());

			//Seperately loading 2 assemblies here rather 
                        //then dynamically loading all in the array.
			//This is because dynamically loading them was causing 
                        //only the last one to be loaded.

			fc.Mappings(m => m.FluentMappings.AddFromAssembly(asmemblies[0]));
			fc.Mappings(m => m.HbmMappings.AddFromAssembly(asmemblies[0]));

			fc.Mappings(m => m.FluentMappings.AddFromAssembly(asmemblies[1]));
			fc.Mappings(m => m.HbmMappings.AddFromAssembly(asmemblies[1]));

			SessionFactory = fc.BuildSessionFactory();
		}
		catch (Exception ex)
		{
			Console.Error.WriteLine(ex);
			throw new Exception("NHibernate initialization failed", ex);
		}
	}
	public static ISession OpenSession()
	{
		return SessionFactory.OpenSession();
	}
	public static ISession GetCurrentSession()
	{
		return SessionFactory.GetCurrentSession();
	}
}

DAO Helper

    public class DATestHelper<TDao, TEntity> : DATestHelper2<TDao, TEntity, long>
        where TDao : GenericDAO<TEntity, long>, new()
    {
    }

    public class DATestHelper2<TDao, TEntity, ID> where TDao : GenericDAO<TEntity, ID>, new()
    {
        public TDao dao { get; private set; }
        public ISession session { get { return (dao != null) ? dao.Session : null; } }

        public DATestHelper2()
        {
            Init();
        }

        // Maps to Test Initialize
        public void Init()
        {
            dao = new TDao();
            dao.Session = NHibernateHelperMock.OpenSession();
            dao.Session.Transaction.Begin();
        }

        // Maps to Test Initialize
        public void InitJoinSession(ISession session)
        {
            dao = new TDao();
            dao.Session = session;
        }

        // Maps to Test Cleanup
        public void Cleanup()
        {
            dao.Session.Transaction.Rollback();
            dao.Session.Close();
            dao = null;
        }
        
        // Maps to Test Cleanup
        public void CleanupJoinedSession()
        {
            dao = null;
        }

        // Maps to Test Cleanup
        public void CommitChangesAndCleanupSession()
        {
            dao.Session.Transaction.Commit();
            dao.Session.Close();
            dao = null;
        }

        public void SaveAndThrowAwayChanges(TEntity entity)
        {
            Save(entity);
            ThrowAwayChanges();                 // Rollback and restart
        }

        public void Save(TEntity entity)
        {
            dao.MakePersistent(entity, false);  // Save without committing
            session.Flush();                    // Force emit the change SQL
        }
        public void DeleteAndThrowAwayChanges(TEntity entity)
        {
            dao.MakeTransient(entity, false);   // Save without committing
            session.Flush();                    // Force emit the change SQL
            ThrowAwayChanges();                 // Rollback and restart
        }

        public void ReopenSession()
        {
            Cleanup();
            Init();
        }

        public void ThrowAwayChanges()
        {
            ReopenSession();
        }
    }

Generic DAO

    public abstract class GenericDAO<T, ID> : IGenericDAO<T, ID>
    {
        private ISession session;
        
        public ISession Session
        {
            get
            {
                if (session == null)
                    session = NHibernateHelper.GetCurrentSession();
                return session;
            }
            set
            {
                session = value;
            }
        }
        
        public virtual T FindById(ID id)
        {
            return Session.Get(id, LockMode.None);
        }
        
        public virtual T FindByIdAndLock(ID id)
        {
            return Session.Load(id, LockMode.Upgrade);
        }
        
        public virtual IList FindAll()
        {
            return Session.CreateCriteria(typeof(T)).List();
        }

        public virtual List GetByCriteria(params ICriterion[] criterion)
        {
            ICriteria criteria = Session.CreateCriteria(typeof(T));

            foreach (ICriterion criterium in criterion)
            {
                criteria.Add(criterium);
            }

            return criteria.List() as List;
        }

        public virtual T MakePersistent(T entity, bool bAutoCommit)
        {
            try
            {
                Session.SaveOrUpdate(entity);
                if (bAutoCommit)
                    Commit();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("MakePersistent - " + typeof(T).Name);
                Debug.WriteLine(ex.Message);
                if (ex.InnerException != null)
                    Debug.WriteLine(ex.InnerException.Message);
                
                throw ex;
            }

            return entity;
        }

        public virtual void MakeTransient(T entity, bool bAutoCommit)
        {
            try
            {
                Session.Delete(entity);
                if (bAutoCommit)
                    Commit();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("MakeTransient - " + typeof(T).Name);
                Debug.WriteLine(ex.Message);
                if (ex.InnerException != null)
                    Debug.WriteLine(ex.InnerException.Message);
                
                throw ex;
            }
        }

        public virtual void Commit()
        {
            Session.Transaction.Commit();
            Session.BeginTransaction();
        }

        public virtual void Rollback()
        {
            Session.Transaction.Rollback();
            Session.BeginTransaction();
        }

        public virtual void Refresh(T entity)
        {
            Session.Refresh(entity);
        }

        public virtual void Evict(T entity)
        {
            try
            {
                Session.Evict(entity);
            }
            catch (KeyNotFoundException)
            {
                //OK to ignore this
            }
        }
        
        public virtual ICriteria CreateCriteria()
        {
            return Session.CreateCriteria(typeof(T));
        }
    }

    public interface IGenericDAO<T, ID>
    {
        T FindById(ID id);
        T FindByIdAndLock(ID id);
        IList FindAll();        
        T MakePersistent(T entity, bool bAutoCommit);
        void MakeTransient(T entity, bool bAutoCommit);
        void Commit();
        void Rollback();
        void Refresh(T entity);
        void Evict(T entity);
        ICriteria CreateCriteria();
    }

Data Access Objects and Domain Entity Objects

public class CustomerDAO : GenericDAO<CustomerDE, long>, ICustomerDAO
    {
        public CustomerDE GetCustomerByName(string customerName)
        {
            CustomerDE ret = null;
            ret = Session.QueryOver()
                .Where(x => x.CustomerFull == customerName).List().FirstOrDefault();
            return ret;
        }
    }
}

public interface ICustomerDAO : IGenericDAO<CustomerDE, long>
{
	CustomerDE GetCustomerByName(string customerName);
}

public class CustomerDE
{
	public virtual long CustomerId { get; set; }
	public virtual string CustomerName { get; set; }
	public virtual string EmailAddress { get; set; }

	public virtual void UpdateCustomer(CustomerDE customer)
	{
		this.CustomerName = customer.CustomerName;
		this.CustomerId = customer.CustomerId;
		this.EmailAddress = customer.EmailAddress;
	}
}

public class ProductDAO : GenericDAO<ProductDE, long>, IProductDAO
{
	public ProductDE GetProductByProductCode(string productCode)
	{
		ProductDE ret = null;

		ret = Session.QueryOver()
			.Where(x => x.ProductCode == productCode).List().FirstOrDefault();

		return ret;
	}
}

public interface IProductDAO : IGenericDAO<ProductDE, long>
{
	ProductDE GetProductByProductCode(string productCode);
}

public class ProductDE
{
	public virtual long ProductId { get; set; }
	public virtual string ProductCode { get; set; }
	public virtual string ProductName { get; set; }
}

Speak Your Mind

*