简体   繁体   English

如何模拟IRepository <T> ?

[英]How to mock IRepository<T>?

I want to mock a unit of work interface with repositories inside for unit test purpose. 我想模拟一个带有存储库的工作单元界面,以进行单元测试。 So far I am able to do it like below. 到目前为止,我能够像下面这样进行操作。

namespace Liquid.Service.UnitTest
{
    using Liquid.DataAccess.Interface;
    using Liquid.Domain;
    using Domain.Interface;
    using Moq;
    using System.Collections.Generic;
    using System.Linq;

    internal class Helper
    {
        internal Mock<IUnitOfWork> MockUnitOfWork(ICollection<Dummy> dummies = null,
            ICollection<ProductType> productTypes = null)
        {
            dummies = dummies ?? new List<Dummy>();
            productTypes = productTypes ?? new List<ProductType>();

            var dummyRepositoryMock = MockDummyRepository(dummies);
            var productTypeRepositoryMock = MockProductTypeRepository(productTypes);

            var unitOfWorkMock = new Mock<IUnitOfWork>();
            unitOfWorkMock.Setup(x => x.DummyRepository)
                .Returns(dummyRepositoryMock.Object);
            unitOfWorkMock.Setup(x => x.ProductTypeRepository)
                .Returns(productTypeRepositoryMock.Object);

            return unitOfWorkMock;
        }

        private Mock<IDummyRepository> MockDummyRepository(ICollection<Dummy> dummies)
        {
            var dummyRepositoryMock = new Mock<IDummyRepository>();

            dummyRepositoryMock.Setup(x => x.FindById(It.IsAny<int>()))
                .Returns((int arg1) => dummies.Where(x => x.Id == arg1).SingleOrDefault());

            dummyRepositoryMock.Setup(x => x.Add(It.IsAny<Dummy>()))
                .Callback((Dummy arg1) => dummies.Add(arg1));

            return dummyRepositoryMock;
        }

        private Mock<IProductTypeRepository> MockProductTypeRepository(ICollection<ProductType> productTypes)
        {
            var productTypeRepositoryMock = new Mock<IProductTypeRepository>();

            productTypeRepositoryMock.Setup(x => x.FindById(It.IsAny<int>()))
                .Returns((int arg1) => productTypes.SingleOrDefault(x => x.Id == arg1));

            productTypeRepositoryMock.Setup(x => x.Add(It.IsAny<ProductType>()))
                .Callback((ProductType arg1) => productTypes.Add(arg1));

            return productTypeRepositoryMock;
        }
    }
}

You see that I've created two method to mock DummyRepository and ProductTypeRepository but because it has same implementation, I think it is redundant for every repositories I have. 您会看到我创建了两个方法来模拟DummyRepository和ProductTypeRepository,但是由于它具有相同的实现,因此我认为对于我拥有的每个存储库都是多余的。 Below is the Repositories and IRepository code. 下面是存储库和IRepository代码。

namespace Liquid.DataAccess.Interface
{
    using Liquid.Domain;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;

    public interface IDummyRepository : IRepository<Dummy>
    {
    }

    public interface IProductTypeRepository : IRepository<ProductType>
    {
    }

    public interface IRepository<TEntity> where TEntity : class
    {
        IList<TEntity> GetAll();

        Task<List<TEntity>> GetAllAsync();

        Task<List<TEntity>> GetAllAsync(CancellationToken cancellationToken);

        IList<TEntity> PageAll(int skip, int take);

        Task<List<TEntity>> PageAllAsync(int skip, int take);

        Task<List<TEntity>> PageAllAsync(CancellationToken cancellationToken, int skip, int take);

        TEntity FindById(object id);

        Task<TEntity> FindByIdAsync(object id);

        Task<TEntity> FindByIdAsync(CancellationToken cancellationToken, object id);

        void Add(TEntity entity);

        void Update(TEntity entity);

        void Remove(TEntity entity);
    }
}

How can I use a same method to mock every repositories implementation which inherits IRepository? 如何使用相同的方法模拟继承IRepository的每个存储库实现?

UPDATE : The test is just a simple add and check like below. 更新:该测试只是一个简单的添加和检查,如下所示。

    [Test]
    public void ProductTypeService_Add()
    {
        // GIVEN
        var productTypeData = new ProductType()
        {
            Id = 1,
            Description = "ProductType1"
        };

        // WHEN
        var unitOfWorkMock = new Helper().MockUnitOfWork();
        var productTypeService = new ProductTypeService(unitOfWorkMock.Object);
        productTypeService.Add(productTypeData);
        unitOfWorkMock.Verify(x => x.SaveChanges());

        // THEN
        Assert.That(productTypeService.FindById(1) != null);
        Assert.That(productTypeService.FindById(2) == null);

        // WHEN
        var productTypeData2 = new ProductType()
        {
            Id = 2,
            Description = "ProductType2"
        };

        productTypeService.Add(productTypeData2);

        // THEN
        Assert.That(productTypeService.FindById(2) != null);
    }

IMHO you are testing the wrong thing; 恕我直言,您正在测试错误的内容; namely you are testing that an in-memory collection (a List<T> ) can store data and the data can be found in the collection. 也就是说,您正在测试内存中的集合( List<T> )可以存储数据,并且可以在集合中找到该数据。 This always yields true because that is the purpose of in-memory collections . 这始终是正确的,因为这是内存收集的目的

Instead of doing this you either need to create integration tests which will use the actual implementation of the underlying repository (such as Entity Framework ) or just test the behavior of your service like this: 无需执行此操作,您需要创建集成测试以使用基础存储库(例如Entity Framework )的实际实现,或者仅测试服务的行为 ,如下所示:

[Test]
public void ProductTypeService_WhenAddingNewItem_CallsSaveChanges()
{
    var unitOfWork = new Mock<IUnitOfWork>();
    // setup the properties of the mock here...

    var service = new ProductTypeService(unitOfWork);
    service.Add(new ProductType { Id = 2, Description = "some product" });

    unitOfWork.AssertWasCalled(_ => _.SaveChanges());
}

This way, you test that your service calls SaveChanges() method; 这样,您可以测试服务是否调用SaveChanges()方法。 actually saving the data is the responsibility of the repository and as I said above, testing that a list can store data in memory is useless. 实际上,保存数据是存储库的责任,正如我上面所说,测试列表可以将数据存储在内存中是没有用的。

I think you have over complicated your question and thus your solution. 我认为您已经使问题和解决方案变得过于复杂。 You don't need interfaces for your various repositories like IDummyRepository and IProductRepository if you are not adding any value to it. 如果不向其添加任何值,则不需要各种存储库(如IDummyRepositoryIProductRepository)的接口。

Your data classes 您的数据类别

public class Dummy
{
    public int Id { get; set; }
}

public class ProductType
{
    public int Id { get; set; }
}

Your ProductTypeService (I can only assume this) 您的ProductTypeService(我只能假设这一点)

public class ProductTypeService
{
    private readonly IUnitOfWork _unitOfWork;

    public ProductTypeService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public void AddProductType(ProductType productType)
    {
        _unitOfWork.ProductTypes.Add(productType);
    }
}

Your IUnitOfWork 您的IUnitOfWork

public interface IUnitOfWork
{
    IRepository<Dummy> Dummies { get; set; }
    IRepository<ProductType> ProductTypes { get; set; }
}

Your IRepository remains unchanged so I won't copy paste it here! 您的IRepository保持不变,因此我不会在此处复制粘贴!

Finally your unit test 最后您的单元测试

[TestFixture]
public class Class1
{
    private Mock<IUnitOfWork> _unitOfWorkMock;
    private Mock<IRepository<Dummy>> _dummyRepositoryMock;
    private Mock<IRepository<ProductType>> _productTypeRepositoryMock;

    [SetUp]
    public void Setup()
    {
        _unitOfWorkMock = new Mock<IUnitOfWork>();
        _dummyRepositoryMock = CreateMock<Dummy>();
        _productTypeRepositoryMock = CreateMock<ProductType>();

        _unitOfWorkMock.Setup(u => u.Dummies).Returns(_dummyRepositoryMock.Object);
        _unitOfWorkMock.Setup(u => u.ProductTypes).Returns(_productTypeRepositoryMock.Object);
    }

    [Test]
    public void product_type_service_should_add_item_to_the_underlying_repository()
    {
        var productTypeService = new ProductTypeService(_unitOfWorkMock.Object);
        var productType = new ProductType {Id = 10};
        productTypeService.AddProductType(productType);
        _productTypeRepositoryMock.Verify(r => r.Add(It.Is<ProductType>(p => p.Id == productType.Id)), Times.Once());
    }

    private Mock<IRepository<T>> CreateMock<T>() where T : class
    {
        var mock = new Mock<IRepository<T>>();

        // do more common magic here

        return mock;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM