Autofac mock - 如何 setup/fake 来自依赖项中特定方法的数据?

Autofac mock - How do I setup/fake data from specific methods in dependencies?

我很可能是单元测试、Autofac 和 mock 的新手,这相对容易,但我很难弄明白。

我有一个 class SystemUnderTest 和两个方法 GetValueOneGetValueTwo.

public class SystemUnderTest : ISystemUnderTest
{
    private readonly IDependency _dependency;

    public SystemUnderTest(IDependency dependency)
    {
        _dependency = dependency;
    }

    public string GetValueOne()
    {
        return _dependency.GetValueOne();
    }

    public string GetValueTwo()
    {
        return _dependency.GetValueTwo();
    }
}

public interface ISystemUnderTest
{
    string GetValueOne();
    string GetValueTwo();
}

这些方法从依赖项中获取数据。

public class Dependency : IDependency
{
    public string GetValueOne()
    {
        return "get-value-one";
    }
    public string GetValueTwo()
    {
        return "get-value-two";
    }
}

public interface IDependency
{
    string GetValueOne();
    string GetValueTwo();
}

我试图从其中一种方法(“GetValueTwo”)伪造数据,因此它返回 "expected value" 而不是 "get-value-two",这通常是依赖性 returns.

[Fact]
public async Task Test_SystemUnderTest()
{
    using (var mock = AutoMock.GetLoose())
    {
        // Setup
        mock.Mock<IDependency>().Setup(x => x.GetValueTwo()).Returns("expected value");

        // Configure
        mock.Provide<IDependency, Dependency>();

        // Arrange - configure the mock
        var sut = mock.Create<SystemUnderTest>();

        // Act
        var actual_GetValueOne = sut.GetValueOne();
        var actual_GetValueTwo = sut.GetValueTwo();

        // Assert - assert on the mock
        Assert.Equal("get-value-one", actual_GetValueOne);
        Assert.Equal("expected value", actual_GetValueTwo);
    }
}

我测试的第一部分,Setup 部分,似乎没有任何效果,可能是因为我做错了一些基本的事情。

有人知道如何拯救我的一天吗?

不是编写单元测试的专家,但我很确定通过使用带有两个类型参数的 Provide,您会覆盖之前所做的 Setup 部分。据我了解,应该使用 Provide 方法来提供您自己的目标接口模拟实现,因此同时使用带有两个类型参数的 Provide 和用于相同依赖项的 Setup 不会感觉。

因此您可以将 Dependency 实现的 GetValueTwo 修改为 return "expected value" 并使用其余未修改的代码,或者您可以将模拟实例提供给具有一种类型参数的 Provide 方法,以及先前设置的两种方法,如下所示:

    [Fact]
    public async Task Test_SystemUnderTest()
    {
        using (var mock = AutoMock.GetLoose())
        {
            var mockedDependency = mock.Mock<IDependency>();

            // Setup
            mockedDependency.Setup(x => x.GetValueOne()).Returns("get-value-one");
            mockedDependency.Setup(x => x.GetValueTwo()).Returns("expected value");

            // The following line is not even necessary
            mock.Provide<IDependency>(mockedDependency.Object);

            // Arrange - configure the mock
            var sut = mock.Create<SystemUnderTest>();

            // Act
            var actual_GetValueOne = sut.GetValueOne();
            var actual_GetValueTwo = sut.GetValueTwo();

            // Assert - assert on the mock
            Assert.Equal("get-value-one", actual_GetValueOne);
            Assert.Equal("expected value", actual_GetValueTwo);
        }
    }

Dependency 实现的成员需要有虚拟成员,以便在进行部分模拟时能够覆盖它们。

public class Dependency : IDependency {
    public virtual string GetValueOne() {
        return "get-value-one";
    }
    public virtual string GetValueTwo() {
        return "get-value-two";
    }
}

然后类似于另一个答案中的建议,您将改为模拟实现,确保使其能够调用基本成员并仅设置您需要覆盖的成员。

public void Test_SystemUnderTest() {
    using (var mock = AutoMock.GetLoose()) {
        // Setup
        var dependency = mock.Mock<Dependency>();
        dependency.CallBase = true;
        dependency.Setup(x => x.GetValueTwo()).Returns("expected value");

        // Configure
        mock.Provide<IDependency>(dependency.Object);

        // Arrange - configure the mock
        var sut = mock.Create<SystemUnderTest>();

        // Act
        var actual_GetValueOne = sut.GetValueOne();
        var actual_GetValueTwo = sut.GetValueTwo();

        // Assert - assert on the mock
        Assert.AreEqual("get-value-one", actual_GetValueOne);
        Assert.AreEqual("expected value", actual_GetValueTwo);
    }
}

如果需要模拟的实现成员可以被覆盖(即virtual),则上述内容在执行时通过。