单元测试 BLL:模拟存储库、UnitOfWork、UnitOfWorkFactory [英] Unit Testing BLL: mock Repository, UnitOfWork, UnitOfWorkFactory
问题描述
我需要快速开始测试业务层上的方法.考虑 Materials BLL 对象,我该如何测试它的 AddNewMaterial 方法?
I need a jump start in testing the methods on my Business layer. Consider the Materials BLL object, how can I test the AddNewMaterial method for it?
interface IGenericRepository<TEntity>
{
TEntity Add(TEntity m);
}
public interface IMaterialRepository : IGenericRepository<Material>
{
}
public interface IUnitOfWork
{
IMaterialRepository Materials { get; private set;}
void Save();
}
public interface IUnitOfWorkFactory
{
IUnitOfWork GetUnitOfWOrk();
}
public class MaterialsBLL
{
private readonly IUnitOfWorkFactory _uowFactory;
//uowFactory comes from DI
public MaterialsBLL(IUnitOfWorkFactory uowFactory)
{
_uowFactory = uowFactory;
}
//TODO: test this
public Material AddNewMaterial(Material m)
{
using(var uow = _uowFactory.GetUnitOfWOrk())
{
var result = uow.Materials.Add(m);
uow.Save();
return result;
}
}
我正在使用 Moq 和 XUnit,但非常环保.一般来说,我想这样做:
I am using Moq, and XUnit, but am very green. In general I want to do this:
- 模拟存储库的 Add 方法.
- 模拟 UoW Materials 属性以返回我的存储库模拟.
- 模拟 UoWFactory 以返回 UoW 模拟.
- 创建 MaterialsBLL,将模拟的 UoWFactory 提供给构造函数.
- 验证 AddNewMaterials 调用存储库的 Add 和 UoW 的 Save 等.
在我看来,我可能应该创建一个 Fake MaterialRepository,而不是嘲笑它?还有什么建议吗?这是第一次破解:
It seems to me that, I maybe should be creating a Fake MaterialRepository, rather than mocking it? Any other advice? Here is a first crack:
[Fact]
public void TestGetMaterialById()
{
var materialList = GetMaterials();
var materialRepositoryMock = new Mock<IMaterialRepository>();
materialRepositoryMock.Setup(repo => repo.Get(4)).Returns(materialList.First());
var uowMock = new Mock<IUnitOfWork>();
uowMock.SetupProperty<IMaterialRepository>(uow => uow.Materials, materialRepositoryMock.Object);
var uowFactoryMock = new Mock<IUnitOfWorkFactory>();
uowFactoryMock.Setup(f => f.GetUnitOfWork()).Returns(uowMock.Object);
var materialsBll = new Materials(uowFactoryMock.Object);
var result = materialsBll.Get(4);
Assert.Equal(result.MaterialId, 4);
Assert.Equal(result.Name, "Four");
}
推荐答案
当您觉得需要多层嵌套模拟对象时,通常是您的设计出了问题.
When you feel like you need several levels of nested mock objects, there's generally something wrong with your design.
德米特法则在此警告我们,您可能不应该修改 uow.Materials
在 MaterialsBLL
中.
The Law of Demeter warns us here that you should probably not tinker with uow.Materials
in MaterialsBLL
.
此外,工作单元通常不是公开存储库的地方.需要访问 Materials 的代码通常会直接引用 IMaterialsRepository
,而不是从 UoW 请求它,然后 Repository 实现可能会在内部引用 UoW.
Besides, a Unit of Work is typically not the place to expose Repositories. The code that needs to access Materials will usually have a direct reference to an IMaterialsRepository
, not ask it from the UoW, and then the Repository implementation might reference the UoW internally.
这会导致更扁平的设计并简化您的生产代码和测试.
This leads to a flatter design and simplifies your production code as well as your tests.
这篇关于单元测试 BLL:模拟存储库、UnitOfWork、UnitOfWorkFactory的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!