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