如何使用 IGenericRepository 进行模拟

How to mock with a IGenericRepository

我是 TDD 开发的新手,我刚刚开始使用 Nunit 3.7.1、Newtonsoft.Json 版本=10.0.3、JustMock Lite 版本 2016.2.426.1、C# 和 .NET 进行一些测试框架 4.7.

我想测试这个class:

public class LoadFinishedTrzlBatch
{
    private IGenericRepository<ProductionOrder> proOrdRepository;
    private IGenericRepository<Batch> batchRepository;
    private IGenericRepository<Code> codeRepository;
    private IGenericRepository<Aggregation> aggregationRepository;
    private IGenericRepository<AggregationChildren> aggChildrenRepository;

    public LoadFinishedTrzlBatch(
        IGenericRepository<ProductionOrder> proOrdRepository,
        IGenericRepository<Batch> batchRepository,
        IGenericRepository<Code> codeRepository,
        IGenericRepository<Aggregation> aggregationRepository,
        IGenericRepository<AggregationChildren> aggChildrenRepository)
    {
        this.proOrdRepository = proOrdRepository;
        this.batchRepository = batchRepository;
        this.codeRepository = codeRepository;
        this.aggregationRepository = aggregationRepository;
        this.aggChildrenRepository = aggChildrenRepository;
    }

    public bool ExistsProductionOrder(string productionOrderName)
    {
        if (string.IsNullOrWhiteSpace(productionOrderName))
            throw new ArgumentNullException(nameof(productionOrderName));

        return (
            proOrdRepository
                .SearchFor(p => p.Name == productionOrderName)
                .FirstOrDefault() != null
        );
    }
}

通过此测试:

[TestFixture]
class LoadFinishedTrzlBatchTest
{
    private LoadFinishedTrzlBatch _load;

    [SetUp]
    public void SetUpLoadFinishedTrzlBatch()
    {
        var proOrdRepository = 
            Mock.Create<IGenericRepository<ProductionOrder>>();
        var batchRepository =
            Mock.Create<IGenericRepository<Batch>>();
        var codeRepository =
            Mock.Create<IGenericRepository<Code>>();
        var aggRepository =
            Mock.Create<IGenericRepository<Aggregation>>();
        var aggChildrenRepository =
            Mock.Create<IGenericRepository<AggregationChildren>>();

        _load = new LoadFinishedTrzlBatch(
            proOrdRepository,
            batchRepository,
            codeRepository,
            aggRepository,
            aggChildrenRepository);
    }

    [Test]
    public void ShouldExistsProductionOrder()
    {
        // Arrange
        Mock.Arrange(() => _load.ExistsProductionOrder("ProOrd"))
            .Returns(true)
            .MustBeCalled();

        // Act
        var actual = _load.ExistsProductionOrder("ProOrd");

        // Assert
        Assert.AreEqual(actual, true);
    }
}

最后是 IGenericRepository:

public interface IGenericRepository<TEntity>
{
    [ OMITTED ]

    IQueryable<TEntity> SearchFor(Expression<Func<TEntity, bool>> predicate);

    [ OMITTED ]
}

首先,我不确定这是否是测试方法 LoadFinishedTrzlBatch.ExistsProductionOrder 的正确方法。 TDD 对我来说一切都是新的,我迷路了。

我对我如何嘲笑 IGenericRepository<ProductionOrder> 感到困惑。它总是正确的,我没有测试 IGenericRepository<ProductionOrder>。可能是因为我没有测试存储库。

我正在学习,但我不知道我在做什么。这就是我问这个问题的原因。我想测试 ExistsProductionOrder 方法。

如何测试 ExistsProductionOrder 方法?

实际上你想 测试 方法 ExistsProductionOrder 是否做了它应该做的事情。因此你不应该模拟它。但是,您想模拟在 方法中调用 的成员,例如对 IGenericRepository<T>.DoSomething().

的任何调用

然而只要调用它并将其结果与您期望的结果进行比较即可:

[Test]
public void ShouldExistsProductionOrder()
{
    // Act
    var actual = _load.ExistsProductionOrder("ProOrd");
    Assert.IsTrue(actual);
}

现在,如果您的方法如下所示:

bool ExistsProductionOrder() {
    this.proOrdRepository.DoSomething();
    return ...
}

您可以将对 DoSomething 的调用替换为不同的东西 - 您的模拟。

你需要将目标依赖分离出来,这样才能在测试用例中隔离安排。您也不要模拟 SUT(被测对象)。你嘲笑它的依赖关系。在这种情况下,您想要模拟通用存储库并安排被调用的方法。即SearchFor

假设该方法采用表达式谓词,您可以将模拟安排为期望一个,然后将其应用于假集合存储以模拟实际数据存储。

[TestFixture]
public class LoadFinishedTrzlBatchTest {
    private LoadFinishedTrzlBatch sut;
    //need this later so declaring as field.
    private IGenericRepository<ProductionOrder> proOrdRepository;

    [SetUp]
    public void SetUpLoadFinishedTrzlBatch() {  
        //Not using these so they can be declared locally          
        var batchRepository =
            Mock.Create<IGenericRepository<Batch>>();
        var codeRepository =
            Mock.Create<IGenericRepository<Code>>();
        var aggRepository =
            Mock.Create<IGenericRepository<Aggregation>>();
        var aggChildrenRepository =
            Mock.Create<IGenericRepository<AggregationChildren>>();

        //initializing target mock
        proOrdRepository = 
            Mock.Create<IGenericRepository<ProductionOrder>>();

        sut = new LoadFinishedTrzlBatch(
            proOrdRepository,
            batchRepository,
            codeRepository,
            aggRepository,
            aggChildrenRepository);
    }

    [Test]
    public void ShouldExistsProductionOrder()
    {
        // Arrange
        var productionOrderName = "ProOrd";
        var orders = new List<ProductionOrder>() {
            new ProductionOrder { Name = productionOrderName },
            new ProductionOrder { Name = "Dummy for Filter" }
        };
        Mock.Arrange(() => proOrdRepository
            .SearchFor(Arg.IsAny<Expression<Func<ProductionOrder,bool>>>()))
            .Returns((Expression<Func<ProductionOrder,bool>> expression) => 
                orders.Where(expression.Compile()).AsQueryable()
            )
            .MustBeCalled();

        // Act
        var actual = sut.ExistsProductionOrder(productionOrderName);

        // Assert
        Assert.IsTrue(actual);
    }
}