4-实体框架单元测试层
在单元测试层中,我们通常测试业务层功能。为了做到这一点,我们将删除数据层(实体框架)依赖项。
现在的问题是:如何删除实体框架依赖关系以便对业务层功能进行单元测试?
答案很简单:我们将为 IDbRepository 接口提供一个假实现,然后我们可以进行单元测试
4-1 实现基本接口(假实现)
class FakeDbRepository : IDbRepository
{
#region Protected Members
protected Hashtable _dbContext;
protected int _numberOfRowsAffected;
protected Hashtable _contextFunctionsResults;
#endregion Protected Members
#region Constractors
public FakeDbRepository(Hashtable contextFunctionsResults = null)
{
_dbContext = new Hashtable();
_numberOfRowsAffected = 0;
_contextFunctionsResults = contextFunctionsResults;
}
#endregion Constractors
#region IRepository Implementation
#region Tables and Views functions
public IQueryable<TResult> GetAll<TResult>(bool noTracking = true) where TResult : class
{
return GetDbSet<TResult>().AsQueryable();
}
public TEntity Add<TEntity>(TEntity entity) where TEntity : class
{
GetDbSet<TEntity>().Add(entity);
++_numberOfRowsAffected;
return entity;
}
public TEntity Delete<TEntity>(TEntity entity) where TEntity : class
{
GetDbSet<TEntity>().Remove(entity);
++_numberOfRowsAffected;
return entity;
}
public TEntity Attach<TEntity>(TEntity entity) where TEntity : class
{
return Add(entity);
}
public TEntity AttachIfNot<TEntity>(TEntity entity) where TEntity : class
{
if (!GetDbSet<TEntity>().Contains(entity))
return Attach(entity);
return entity;
}
#endregion Tables and Views functions
#region Transactions Functions
public virtual int Commit()
{
var numberOfRowsAffected = _numberOfRowsAffected;
_numberOfRowsAffected = 0;
return numberOfRowsAffected;
}
public virtual Task<int> CommitAsync(CancellationToken cancellationToken = default(CancellationToken))
{
var numberOfRowsAffected = _numberOfRowsAffected;
_numberOfRowsAffected = 0;
return new Task<int>(() => numberOfRowsAffected);
}
#endregion Transactions Functions
#region Database Procedures and Functions
public virtual TResult Execute<TResult>(string functionName, params object[] parameters)
{
if (_contextFunctionsResults != null && _contextFunctionsResults.Contains(functionName))
return (TResult)_contextFunctionsResults[functionName];
throw new NotImplementedException();
}
#endregion Database Procedures and Functions
#endregion IRepository Implementation
#region IDisposable Implementation
public void Dispose()
{
}
#endregion IDisposable Implementation
#region Private Functions
private List<TEntity> GetDbSet<TEntity>() where TEntity : class
{
if (!_dbContext.Contains(typeof(TEntity)))
_dbContext.Add(typeof(TEntity), new List<TEntity>());
return (List<TEntity>)_dbContext[typeof(TEntity)];
}
#endregion Private Functions
}
4-2 运行单元测试
[TestClass]
public class ProductUnitTest
{
[TestMethod]
public void TestInsertForNewCategory()
{
// Initialize repositories
FakeDbRepository _dbRepository = new FakeDbRepository();
// Initialize Business object
IProductBusiness productBusiness = new ProductBusiness(_dbRepository);
// Process test method
productBusiness.InsertForNewCategory("Test Product", "Test Category");
int _productCount = _dbRepository.GetAll<Product>().Count();
int _categoryCount = _dbRepository.GetAll<Category>().Count();
Assert.AreEqual<int>(1, _productCount);
Assert.AreEqual<int>(1, _categoryCount);
}
[TestMethod]
public void TestProceduresFunctionsCall()
{
// Initialize Procedures / Functions result
Hashtable _contextFunctionsResults = new Hashtable();
_contextFunctionsResults.Add("GetProductsCategory", new List<GetProductsCategory_Result> {
new GetProductsCategory_Result() { ProductName = "Product 1", ProductID = 1, CategoryName = "Category 1" },
new GetProductsCategory_Result() { ProductName = "Product 2", ProductID = 2, CategoryName = "Category 1" },
new GetProductsCategory_Result() { ProductName = "Product 3", ProductID = 3, CategoryName = "Category 1" }});
// Initialize repositories
FakeDbRepository _dbRepository = new FakeDbRepository(_contextFunctionsResults);
// Initialize Business object
IProductBusiness productBusiness = new ProductBusiness(_dbRepository);
// Process test method
var results = productBusiness.GetProductsCategory(1);
Assert.AreEqual<int>(3, results.Count());
}
}