简体   繁体   English

使用Moq模拟具体类方法的返回值

[英]Mocking return values of a concrete class method using Moq

I have an abstract factory like this. 我有一个像这样的抽象工厂。

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

And I have its concrete implementation like this. 我有这样的具体实现。

public class SyncFactory : AbstractFactory
{
    private readonly IOperatorRepository _operatorRepository;

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

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

This concrete factory is accessed in a method like this. 通过这种方法可以访问该具体工厂。

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

Now I need to write a unit test for MethodTobeTested(). 现在,我需要为MethodTobeTested()编写单元测试。

I mocked the return value of the GetSyncService() like this. 我像这样模拟了GetSyncService()的返回值。 But it's calling the actual OperatorSyncService, not the mock. 但这是在调用实际的OperatorSyncService,而不是模拟。 I need this mock to mock another method inside OperatorSyncService 我需要这个模拟来模拟OperatorSyncService中的另一个方法

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

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

Any idea on how to resolve this? 关于如何解决这个问题的任何想法?

In your SyncFactory implementation you inject an instance of IOperatorRepository. 在SyncFactory实现中,您将注入IOperatorRepository的实例。 This is great because it allows you to inject a different version if needs be and creates a seem for you to use a mock implementation of IOperatorRepository. 这很棒,因为它允许您在需要时注入其他版本,并为您创建一个使用IOperatorRepository的模拟实现的外观。

You have also made an abstract factory which looks good but it looks like the problem is with your usage of the factory; 您还创建了一个看起来不错的抽象工厂,但问题似乎出在工厂的使用上。

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

In your MethodToBeTested you create a concrete implementation of SyncFactory, this make the point of the abstract factory a little redundent since you cannot inject a different implementation. 在您的MethodToBeTested中,您将创建SyncFactory的具体实现,由于您无法注入其他实现,因此使抽象工厂的要点变得多余。 I don't know where you are geting your _operatorRepository instance from but I can see two ways forward. 我不知道您从哪里获取_operatorRepository实例,但是我可以看到两种方法。

  1. In the constructor of the class that contains MethodToBeTested add a parameter that takes an instance of your abstract factory, then get your MethodToBeTested to use this injected factory instead of creating a new one, this would allow you to mock the whole factory - this is my recommended approach because the class containing MethodToBeTested would no longer need to know how to create a factory instance which it shouldn't if you are following the single responsibility principle. 在包含MethodToBeTested的类的构造函数中,添加一个采用抽象工厂实例的参数,然后让您的MethodToBeTested使用注入的工厂而不是创建一个新工厂,这将允许您模拟整个工厂-这是我的推荐的方法,因为包含MethodToBeTested的类将不再需要知道如何创建工厂实例,如果您遵循单一职责原则,则不应该创建该实例。 There would be no dependence on any concrete implementation. 不会依赖任何具体的实现。

  2. As above but instead inject IOperatorRepository rather than the factory, you can then inject a mock IOperatorRepository but I would advise against this since you've done such a good job of creating all your abstractions to then cast this work aside and "new up" an instance of syncFactory and creating a concrete dependency 如上所述,但是可以注入IOperatorRepository而不是工厂,而后可以注入模拟的IOperatorRepository,但是我建议您这样做,因为您已经做得很好,可以创建所有抽象,然后将其抛弃并“重新构建”实例并创建一个具体的依赖

MethodToBeTested is tightly coupled to SyncFactory because the method is manually creating a new instance of SyncFactory . MethodToBeTestedSyncFactory紧密耦合,因为该方法是手动创建SyncFactory的新实例。 This makes mocking the dependency very difficult. 这使得模拟依赖非常困难。

Assuming 假设

public class ClassToBeTested {

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

}

ClassToBeTested should be refactored to 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
    }

}

This will allow the dependency to be mocked and injected into the class to be tested and accessed by the method to be tested. 这将允许模拟依赖项并将其注入要测试的类,并由要测试的方法访问。 The class to be tested now only needs to know what it needs to know. 现在,要测试的类仅需要知道它需要知道的内容。 It now no longer needs to be aware of IOperatorRepository . 现在,它不再需要知道IOperatorRepository

The new in the method MethodTobeTested creates new instance so no mock can be injected. new的方法MethodTobeTested创建新实例,所以没有模拟可以注射。 Inject the factory eg as parameter so it can be mocked in the test. 注入工厂作为参数,以便可以在测试中模拟它。

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
    // ...
}

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

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