单元测试BLL:mock Repository,UnitOfWork,UnitOfWorkFactory

时间:2014-05-08 06:17:07

标签: unit-testing mocking three-tier

我需要一个快速入门来测试我的业务层上的方法。考虑Materials BLL对象,我该如何为它测试AddNewMaterial方法?

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,但我非常环保。一般来说,我想这样做:

  1. 模拟存储库添加方法。
  2. 模拟UoW Materials属性以返回我的存储库模拟。
  3. 模拟UoWFactory以返回UoW模拟。
  4. 创建MaterialsBLL,将模拟的UoWFactory提供给contstructor。
  5. 验证AddNewMaterials是否调用了存储库的Add,以及UoW的Save等。
  6. 在我看来,我可能应该创建一个Fake MaterialRepository,而不是嘲笑它?还有其他建议吗?这是第一次破解:

        [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");
        }
    

1 个答案:

答案 0 :(得分:0)

如果您觉得需要多个级别的嵌套模拟对象,那么您的设计通常会出现问题。

Law of Demeter在此向我们发出警告,您可能不应该修改uow.Materials中的MaterialsBLL

此外,工作单元通常不是公开存储库的地方。需要访问Materials的代码通常直接引用IMaterialsRepository,而不是从UoW请求它,然后Repository实现可能会在内部引用UoW。

这样可以实现更平坦的设计,简化您的生产代码和测试。