简体   繁体   English

class 中使用 Autofixture、Moq 和 XUnit 的部分模拟方法

[英]Partial mock methods in a class using Autofixture, Moq and XUnit

I want to mock only some methods of a class and call the real implementation for other methods.我只想模拟 class 的一些方法,并为其他方法调用真正的实现。

I have my sut class Test where the Runner class is injected in the constructor.我有我的 sut class 测试,其中 Runner class 被注入到构造函数中。 This injected class has again a injected other class RunnerParam in the constructor.这个注入的 class 在构造函数中再次注入了其他 class RunnerParam。

The code is a simplified case of my real classes in trying to have only the basics.该代码是我尝试仅具有基础知识的真实课程的简化案例。

[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
}

Supporting members支持成员

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);
    }
}

I want to mock and give a mocked value to the method Run in the class Runner, but to use the real implementation of the method RunImplementation.我想模拟并给 class Runner 中的方法 Run 一个模拟值,但要使用方法 RunImplementation 的真正实现。

I would expect to see for result2 500, but it's 0, meaning that the method is not seen as mocked up.我希望看到 result2 500,但它是 0,这意味着该方法不被视为模拟。 In my eyes that is correct, but the Moq callbase is equal to true, so the real implementation should be taken, but it isn't.在我看来,这是正确的,但 Moq callbase 等于 true,所以应该采用真正的实现,但事实并非如此。

What am I missing here?我在这里想念什么?

In the shown simplified example, Test is only dependent on IRunner在所示的简化示例中, Test仅依赖于IRunner

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

So that is all that needs to be mocked if the intention was to test Test class in isolation.因此,如果打算单独测试Test class,那么这就是所有需要嘲笑的东西。

//...

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

//...

If Runner class is to be also tested in isolation, then a mocked RunnerParam would be needed to satisfy its dependencies.如果要单独测试Runner class,则需要一个模拟的RunnerParam来满足其依赖关系。

It should however be dependent on the abstraction (interface) and not the concretion (implementation).然而,它应该依赖于抽象(接口)而不是具体(实现)。

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

This simplifies the isolated test as described in the original question这简化了原始问题中描述的隔离测试

I want to mock and give a mocked value to the method Run in the class Runner, but to use the real implementation of the method RunImplementation.我想模拟并给 class 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);

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

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