【问题标题】:Moq Setup override起订量设置覆盖
【发布时间】:2019-08-19 00:51:13
【问题描述】:

我已经浏览了所有以前的答案,但没有一个能解决我的问题。

假设我有以下代码:

public interface ISomeInterface
{
    int SomeMethod(int a, string b);
}

现在我有一个通用的模拟类,它为上述方法定义了一些默认行为

public class CommonMock
{
    public Mock<ISomeInterface> MockInterface = new Mock<ISomeInterface>().Setup(x => x.SomeMethod(It.IsAny<int>(), It.IsAny<string>())).Returns(It.IsAny<int>());
}

我需要一些默认行为,因为我有很多需要默认行为的测试用例。

但是在某些特定的测试场景中,在一个完全独立的测试类中,我需要能够在测试某些特定的测试用例时返回不同的值。

如下所示:

[Test]
public void TestSomeMethodSpecific()
{
    var commonMock = new CommonMock();
    commonMock.MockInterface.Setup(x => x.SomeMethod(It.IsAny<int>(), It.IsAny<string>())).Returns(42);

    // Do some test based on the new return value
}

我怎样才能做到这一点?

下面我附上一些实际代码:

通用设置

public class MockStore
{
    public Mock<IProcessHandler> ProcessHandler = new Mock<IProcessHandler>();
    ProcessHandler.Setup(x => x.GetCurrentProcessRunId()).Returns(It.IsAny<int>());
}

测试类中的覆盖设置

var mockstore = new MockStore();
mockStore.ProcessHandler.Setup(x => x.GetCurrentProcessRunId()).Returns(25);

并且有近 50 到 70 个这样的模拟,每个模拟从简单类型返回到复杂类。

【问题讨论】:

  • 作为解决方法,您可以实现类似 MoqValueProvider 之类的东西,它在大多数情况下返回公共值,在特定情况下返回特定值,并在 Moq 设置中获取此值
  • 你可以使用TestInitialize方法。
  • 只需在每次测试之前在 SetUp 方法中创建默认模拟,然后在测试中您可以随意更改任何您想要的内容,它将被限制在该测试的范围内......
  • 不幸的是,这不是一个可行的解决方案,因为在生产环境中大约有 2000 多个测试。这就是实现默认模拟行为的原因。
  • @vivek86 我不明白你...默认实现将在SetUp 方法中,你可以在每次测试中免费获得它,然后你只能覆盖你需要的东西,因为moq按照这个原则工作,最后的设置获胜......

标签: c# moq


【解决方案1】:

应该可以吗?如果您在方法上创建后续设置并且它是非条件的(对参数没有约束),那么它将删除该方法的所有先前设置。

你可以看到my answer here that explains it with the source code

如果您想要多个有条件的设置,根据参数返回不同的值,请参阅How to setup a method twice for different parameters with mock

没有看到您的完整代码,也许您已经在使用条件设置。在这种情况下,顺序很重要,也许您正在用更通用的设置覆盖较早的设置。

【讨论】:

    【解决方案2】:

    您可以随时修改全局模拟对象。例如我有这个:

    [TestClass]
    public class StoreServiceTest
    {
        Mock<IAccess> mockAccess;
        Mock<IAccess> mockAccessNoData;
        Mock<IDataReader> mockReader;
        Mock<IDataReader> mockReaderNoData;
        Mock<IStoreService> mockStoreService;
    

    然后对TestInitiailize,我Setup默认实现如下:

    mockReader = new Mock<IDataReader>();
    mockReader.Setup(m => m.IsDBNull(It.IsAny<int>())).Returns(false);
    mockReader.Setup(m => m.GetString(It.IsAny<int>())).Returns("stub");
    mockReader.Setup(m => m.GetBoolean(It.IsAny<int>())).Returns(true);
    mockReader.Setup(m => m.GetInt32(It.IsAny<int>())).Returns(32);
    mockReader.SetupSequence(m => m.Read()).Returns(true).Returns(false); // setup sequence to avoid infinite loop
    
    mockAccess = new Mock<IAccess>();
    mockAccess.Setup(m => m.ReadData(It.IsAny<string>(), It.IsAny<object[]>())).Returns(mockReader.Object);
    
    mockReaderNoData = new Mock<IDataReader>();
    mockReaderNoData.Setup(m => m.Read()).Returns(false);
    
    mockAccessNoData = new Mock<IAccess>();
    mockAccessNoData.Setup(m => m.ReadData(It.IsAny<string>(), It.IsAny<object[]>())).Returns(mockReaderNoData.Object);
    
    mockStoreService = new Mock<IStoreService>(); 
    

    现在对于默认类型的测试,我所做的只是通过mockReader.Object,它应该具有默认实现,因为每个测试都以TestInitialize 开头,然后对于特殊情况,假设我想返回@987654327 @ 而不是"stub"IDataReaderGetString() 方法,我可以这样做:

    mockReader.Setup(m => m.GetString(It.IsAny<int>())).Returns((int col) => {
        if (col == 2) return "sub";
        else return "stub";
    });
    

    希望有帮助!

    【讨论】:

      猜你喜欢
      • 2010-12-30
      • 2012-08-26
      • 2010-11-30
      • 1970-01-01
      • 2010-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多