使用Moq

时间:2016-10-12 08:39:08

标签: c# unit-testing moq abstract-class concreteclass

我有一个像这样的抽象工厂。

public abstract class AbstractFactory
{
    public abstract ISyncService GetSyncService(EntityType entityType);
}

我有这样的具体实现。

public class SyncFactory : AbstractFactory
{
    private readonly IOperatorRepository _operatorRepository;

    public SyncFactory( IOperatorRepository operatorRepository)
    {
        _operatorRepository = operatorRepository;
    }

    public override ISyncService GetSyncService(EntityType entityType)
    {            
            return new OperatorSyncService(_operatorRepository);           
    }
}

可以通过这样的方法访问此具体工厂。

public void MethodTobeTested()
{
    var syncService =
                new SyncFactory(_operatorRepository).GetSyncService(entityType);
}

现在我需要为MethodTobeTested()编写一个单元测试。

我像这样模拟了GetSyncService()的返回值。但它调用的是实际的OperatorSyncService,而不是mock。我需要这个模拟来模拟OperatorSyncService中的另一个方法

private Mock<SyncFactory> _syncServiceMock;
_syncServiceMock = new Mock<SyncFactory>();

_syncServiceMock.Setup(m => m.GetSyncService(operator)).Returns(_operatorSyncServiceMock.Object);

有关如何解决这个问题的想法吗?

3 个答案:

答案 0 :(得分:2)

在SyncFactory实现中,您将注入IOperatorRepository的实例。这很好,因为它允许您在需要时注入不同的版本,并为您创建一个似乎可以使用IOperatorRepository的模拟实现。

您还制作了一个看起来不错的抽象工厂,但看起来问题在于您对工厂的使用;

var syncService =
            new SyncFactory(_operatorRepository).GetSyncService(entityType);

在您的MethodToBeTested中,您创建了一个SyncFactory的具体实现,这使得抽象工厂的重点变得有些重要,因为您无法注入不同的实现。我不知道你在哪里制作你的_operatorRepository实例,但我可以看到两种方法。

  1. 在包含MethodToBeTested的类的构造函数中添加一个参数,该参数接受抽象工厂的实例,然后让您的MethodToBeTested使用这个注入的工厂而不是创建一个新工厂,这将允许您模拟整个工厂 - 这是我推荐的方法,因为包含MethodToBeTested的类不再需要知道如何创建工厂实例,如果您遵循单一责任原则则不应该知道它。不依赖任何具体实施。

  2. 如上所述但是注入IOperatorRepository而不是工厂,你可以注入一个模拟的IOperatorRepository,但我会建议不要这样做,因为你已经做了很好的工作来创建所有的抽象然后把这个工作抛到一边和“new up”一个syncFactory实例并创建一个具体的依赖

答案 1 :(得分:1)

MethodToBeTestedSyncFactory紧密相关,因为该方法是手动创建SyncFactory的新实例。这使得模仿依赖非常困难。

假设

public class ClassToBeTested {

    public void MethodTobeTested() {
        var syncService = new SyncFactory(_operatorRepository).GetSyncService(entityType);
        //...other code
    }

}

ClassToBeTested应该重构为

public class ClassToBeTested {
    private readonly AbstractFactory syncFactory;

    public ClassToBeTested (AbstractFactory factory) {
        this.syncFactory = factory
    }

    public void MethodTobeTested() {
        var syncService = syncFactory.GetSyncService(entityType);
        //...other code
    }

}

这将允许模拟依赖项并将其注入到要测试的方法进行测试和访问的类中。现在要测试的课程只需要知道它需要知道什么。它现在不再需要了解IOperatorRepository

答案 2 :(得分:0)

方法new中的MethodTobeTested创建新实例,因此不能注入模拟。注入工厂,例如作为参数,因此可以在测试中进行模拟。

public void MethodTobeTested(AbstractFactory factory)
{
    EntityType entityType = null;
    var syncService = factory.GetSyncService(entityType);
}

[TestMethod]
public void Method_Condition_Result()
{
    // Arrange
    TestedClass tested = new TestedClass();
    Mock<ISyncService> syncServiceMock = new Mock<ISyncService>();
    Mock<AbstractFactory> factoryMock = new Mock<AbstractFactory>();
    factoryMock.Setup(f => f.GetSyncService(It.IsAny<EntityType>())).Returns(syncServiceMock.Object);

    // Act
    tested.MethodTobeTested(factoryMock.Object);

    // Assert
    // ...
}