【问题标题】:Partial mock methods in a class using Autofixture, Moq and XUnit使用 Autofixture、Moq 和 XUnit 的类中的部分模拟方法
【发布时间】:2020-02-18 18:52:29
【问题描述】:

我只想模拟一个类的一些方法,并为其他方法调用真正的实现。

我有我的 sut 类 Test,其中 Runner 类被注入到构造函数中。这个注入的类在构造函数中再次注入了另一个类 RunnerParam。

代码是我的真实课程的简化案例,试图只拥有基础知识。

[Fact]
public void Test()
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());

    var paramMock = fixture.Freeze<Mock<IRunnerParam>>();
    paramMock.Setup(x => x.Multiplicator()).Returns(2);

    var classMock = fixture.Freeze<Mock<IRunner>>();
    classMock.Setup(x => x.Run()).Returns(5);

    var test = fixture.Create<Test>();

    var result = test.StartRunning();  // should be 5
    var result2 = test.StartRunningImplementation(5); // should be 500
}

支持成员

public interface IRunnerParam
{
    int Multiplicator();
}

public class RunnerParam : IRunnerParam
{
    public virtual int Multiplicator()
    {
        return 20;
    }
}

public interface IRunner
{
    int Run();
    int RunImplementation(int param);
}

public class Runner : IRunner
{
    protected virtual RunnerParam MultiParam { get; set; }
    public Runner(RunnerParam multiParam)
    {
        MultiParam = multiParam;
    }

    public virtual int Run()
    {
        return 10;
    }

    public int RunImplementation(int param)
    {
        return 10 * MultiParam.Multiplicator() * param * Run();
    }
}

public class Test
{
    private readonly IRunner _runner;
    public Test(IRunner runner)
    {
        _runner = runner;
    }

    public int StartRunning()
    {
        return _runner.Run();
    }

    public int StartRunningImplementation(int param)
    {
        return _runner.RunImplementation(param);
    }
}

我想模拟并给类Runner中的方法Run一个模拟值,但是要使用方法RunImplementation的真正实现。

我希望看到 result2 500,但它是 0,这意味着该方法不被视为模拟。在我看来这是正确的,但是 Moq callbase 等于 true,所以应该采用真正的实现,但事实并非如此。

我在这里错过了什么?

【问题讨论】:

    标签: c# unit-testing moq xunit autofixture


    【解决方案1】:

    在所示的简化示例中,Test 仅依赖于 IRunner

    private readonly IRunner _runner;
    public Test(IRunner runner)
    {
        _runner = runner;
    }
    

    如果打算单独测试Test 类,那么这就是所有需要嘲笑的东西。

    //...
    
    var classMock = fixture.Freeze<Mock<IRunner>>();
    classMock.Setup(x => x.Run()).Returns(5);
    classMock.Setup(x => x.RunImplementation(It.IsAny<int>())).Returns(500);
    
    //...
    

    如果还要单独测试Runner 类,则需要模拟的RunnerParam 来满足其依赖关系。

    然而,它应该依赖于抽象(接口)而不是具体(实现)。

    protected virtual IRunnerParam MultiParam { get; set; }
    public Runner(IRunnerParam multiParam) {
        MultiParam = multiParam;
    }
    

    这简化了原始问题中描述的孤立测试

    我想模拟并给类Runner中的方法Run一个模拟值,但是要使用方法RunImplementation的真正实现。

    //Arrange
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    
    var runnerParam = fixture.Freeze<Mock<IRunnerParam>>()
        .Setup(_ => _.Multiplicator())
        .Returns(2);
    
    var subjectMock = fixture.Freeze<Mock<Runner>>();
    subjectMock.CallBase = true;
    subjectMock.Setup(_ => _.Run()).Returns(5);
    
    int expected = 500;
    Runner sut = subjectMock.Object;
    
    //Act
    var actual = sut.RunImplementation(5); // should be 500
    
    //Assert
    actual.Should().Be(expected);
    

    【讨论】:

    • 我明白你的意思。这确实是要走的路,一个一个地测试这些类,而不是试图在一个测试中完成它们。
    猜你喜欢
    • 2014-01-24
    • 1970-01-01
    • 1970-01-01
    • 2015-02-17
    • 2012-06-16
    • 2022-12-22
    • 2021-12-04
    • 1970-01-01
    • 2019-09-03
    相关资源
    最近更新 更多