在单元测试中,当使用nSubstitute和Autofixture作为DI容器时,如何获取模拟对象?

Mar*_*rio 3 c# unit-testing dependency-injection autofixture nsubstitute

我曾经在单元测试中使用 Moq 和 AutoMoqer,但我的团队决定改为 NSubstitute。我们大量使用 DI,因此我希望能够请求一个目标进行测试,并让该目标自动将所有模拟对象提供给其构造函数,或者换句话说,是一个传入模拟的 DI 容器。我还想根据需要修改这些模拟对象。

使用 Moq/AutoMoq/MSTest 的示例

[TestMethod]
public void ReturnSomeMethod_WithDependenciesInjectedAndD1Configured_ReturnsConfiguredValue()
{
    const int expected = 3;
    var diContainer = new AutoMoq.AutoMoqer();

    var mockedObj = diContainer.GetMock<IDependency1>();
    mockedObj
        .Setup(mock => mock.SomeMethod())
        .Returns(expected);

    var target = diContainer.Resolve<MyClass>();
    int actual = target.ReturnSomeMethod();

    Assert.AreEqual(actual, expected);
}

public interface IDependency1
{
    int SomeMethod();
}

public interface IDependency2
{
    int NotUsedInOurExample();
}

public class MyClass
{
    private readonly IDependency1 _d1;
    private readonly IDependency2 _d2;

    //please imagine this has a bunch of dependencies, not just two
    public MyClass(IDependency1 d1, IDependency2 d2)
    {
        _d1 = d1;
        _d2 = d2;
    }

    public int ReturnSomeMethod()
    {
        return _d1.SomeMethod();
    }
}
Run Code Online (Sandbox Code Playgroud)

由于我的问题措辞不佳并且我进行了更多研究,我找到了一种使用 NSubstitute/AutofacContrib.NSubstitute/XUnit 来实现此目的的方法:

[Fact]
public void ReturnSomeMethod_WithDependenciesInjectedAndD1Configured_ReturnsConfiguredValue()
{
    const int expected = 3;
    var autoSubstitute = new AutoSubstitute();
    autoSubstitute.Resolve<IDependency1>().SomeMethod().Returns(expected);

    var target = autoSubstitute.Resolve<MyClass>();
    int actual = target.ReturnSomeMethod();

    Assert.Equal(actual, expected);
}

public interface IDependency1
{
    int SomeMethod();
}

public interface IDependency2
{
    int NotUsedInOurExample();
}

public class MyClass
{
    private readonly IDependency1 _d1;
    private readonly IDependency2 _d2;

    //please imagine this has a bunch of dependencies, not just two
    public MyClass(IDependency1 d1, IDependency2 d2)
    {
        _d1 = d1;
        _d2 = d2;
    }

    public int ReturnSomeMethod()
    {
        return _d1.SomeMethod();
    }
}
Run Code Online (Sandbox Code Playgroud)

我仍然有我原来的问题。如何使用 AutoFixture.AutoNSubstitute 作为 DI 容器来执行此操作?

Mar*_*ann 6

您可以将 AutoFixture 变成具有各种动态模拟库(包括 NSubstitute)的自动模拟容器

重写 OP 测试就像这样简单:

[Fact]
public void ReturnSomeMethod_UsingAutoFixtureAutoNSubstitute()
{
    const int expected = 3;
    var fixture = new Fixture().Customize(new AutoNSubstituteCustomization());
    fixture.Freeze<IDependency1>().SomeMethod().Returns(expected);

    var target = fixture.Create<MyClass>();
    var actual = target.ReturnSomeMethod();

    Assert.Equal(actual, expected);
}
Run Code Online (Sandbox Code Playgroud)

在这里,我使用了xUnit.net而不是 MSTest,但翻译它应该很简单。

这里的关键是方法Freeze,它本质上将类型参数的生命周期从瞬态更改为单例。这意味着在Freeze调用之后,特定Fixture实例必须创建IDependency对象的所有其他时间,它将重用它冻结的对象。

Freeze还返回刚刚冻结的对象,这使您能够轻松地对其进行“点注” - 在本例中通过使用 NSubstitute 的 API。