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