简体   繁体   English

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

[英]In unit test when using nSubstitute and Autofixture as a DI container, how to get mocked object?

I used to use Moq and AutoMoqer in unit tests, but my team has decided to change to NSubstitute. 我曾经在单元测试中使用Moq和AutoMoqer,但是我的团队决定更改为NSubstitute。 We use DI heavily, so I'd like to be able to ask for a target to test and have that target automatically given all mocked objects to its constructor, or in other words a DI container that passes in mocks. 我们大量使用了DI,所以我希望能够请求一个目标进行测试,并让该目标自动将所有模拟对象分配给其构造函数,或者换句话说,一个传递模拟的DI容器。 I also want to modify those mocked objects as necessary. 我还想根据需要修改那些模拟对象。

Example using Moq/AutoMoq/MSTest 使用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();
    }
}

Since my question was poorly worded and I have researched a bit more, I have found a way to get this working using NSubstitute/AutofacContrib.NSubstitute/XUnit: 由于我的问题措辞不佳,并且我进行了更多研究,因此我找到了一种使用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();
    }
}

I still have my original question. 我仍然有我最初的问题。 How can I do this using AutoFixture.AutoNSubstitute as a DI container? 如何使用AutoFixture.AutoNSubstitute作为DI容器来执行此操作?

You can turn AutoFixture into an Auto-Mocking Container with various dynamic mock libraries, including NSubstitute. 您可以使用各种动态模拟库(包括NSubstitute)将AutoFixture变成自动模拟容器

Rewriting the OP test is as easy as this: 重写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);
}

Here, I've used xUnit.net instead of MSTest, but it ought to be trivial to translate it. 在这里,我使用的是xUnit.net而不是MSTest,但翻译它应该很简单。

The key here is the Freeze method, which essentially changes the lifetime of the type argument from transient to singleton . 这里的关键是Freeze方法,该方法实质上将类型参数的生存期从transient更改为singleton This means that after Freeze has been called, all other times that particular Fixture instance has to create an IDependency object, it'll reuse the object it froze. 这意味着在调用Freeze之后,在其他所有特定Fixture实例必须创建IDependency对象的情况下,它将重用冻结的对象。

Freeze also returns the object it just froze, which enables you to easily keep 'dotting' into it - in this case by using NSubstitute's API. Freeze还返回刚刚冻结的对象,这使您可以轻松地“添加”到该对象中(在这种情况下,使用NSubstitute的API)。

I little bid confused when you call Autofixture a Dependency Injection container. 当您将Autofixture称为Dependency Injection容器时,我很少感到困惑。 It is not. 它不是。

AutoFixture is simply generate values/instances you don't care about in current test, but they cannot hold default values. AutoFixture只是生成当前测试中不需要关心的值/实例,但是它们不能保存默认值。

In your case, you correctly created mock of IPromise , but class under test doesn't know about it. 在您的情况下,您正确创建了IPromise模拟,但是被测试的类对此一无所知。 AutoFixture have generated "own" instance and pass it to class under test. AutoFixture已生成“自己的”实例并将其传递给测试中的类。

You should create instance of class under test manually - because you need full control of all dependencies - for documentation reasons, where other developers can see how object created 您应该手动创建被测类的实例-因为您需要完全控制所有依赖项-出于文档原因,其他开发人员可以在其中查看如何创建对象

You can use AutoFixture for dependencies you do not care about in the particular test, but for getting class under test work properly this dependency should return some value (not null or default values). 您可以将AutoFixture用于在特定测试中不关心的依赖项,但是为了使类在测试中正常工作,此依赖项应返回一些值(不是null或默认值)。

// Arrange
var fixture = New Fixture();

var input = fixture.Create<int>(); // Will generate some integer value
var expected = fixture.Create<string>();

var dummyDependency = fixture.Create<IPromiseOne>(); // Don't care about in this test
var requiredDependency = Substitute.For<IPromiseTwo>(); // Dependency required in the test

// return "expected" only when "input" given
requiredDependency.SomeFunction(input).Returns(expected); 

var classUnderTest = new ClassUnderTest(requiredDependency, dependencyTwo);

// Act
var actual = classUnderTest.Convert(input);

// Assert
actual.Should().Be(expected);

For multiple tests you can introduce some factory class which create instance of class under the test and expose used dependencies as public properties, so you have access to them in the test. 对于多个测试,您可以引入一些工厂类,这些工厂类在测试下创建类的实例,并将使用的依赖项公开为公共属性,因此您可以在测试中访问它们。
Or use dependencies as private members of test class, so all tests have access to them. 或将依赖项用作测试类的私有成员,以便所有测试都可以访问它们。

Of course AutoFixture provide possibility to configuration possibilities. 当然,AutoFixture提供了配置可能性。 So you can configure to always return "your" mock when some type is asked by fixture 因此,您可以配置为在灯具要求某种类型时始终返回“您的”模拟

var fixture = new Fixture();

var myMock = Substitute.For<IPromise>();
myMock.SomeFunction().Returns(default(ReturnClass)) // default value is null

// Configure fixture to always return your mock
fixture.Register<IPromise>(() => myMock);

var classUnderTest = fixture.Create<ClassUnderTest>();

// Execute test

However you should be careful when creating class under test with AutoFixture, because AutFixture will generate random values for all public properties, which can be undesired, because for testing you need full control of your class under test. 但是,使用AutoFixture创建被测类时要小心,因为AutFixture会为所有公共属性生成随机值,这是不希望的,因为对于测试,您需要完全控制被测类。

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

相关问题 如何使用NSubstitute和/或AutoFixture来测试具体类 - How to use NSubstitute and/or AutoFixture to test a concrete class 实体框架6单元测试(NSubstitute) - 如何测试带有孩子的对象在没有请求孩子时不返回孩子 - Entity Framework 6 Unit Testing (NSubstitute) - How to test that an object with children does not return children when children are not requested 使用Autofixture Generator时单元测试间歇性OOM异常 - Unit test intermittent OOM exception when using Autofixture Generator 如何使用 NSubstitute 在单元测试中模拟 MassTransit Header 值 - How to mock MassTransit Header values in unit test using NSubstitute 如何将 AutoFixture 与 NSubstitute 结合使用的示例 - Example of how to use AutoFixture with NSubstitute 使用NUnit和NSubstitute对通用存储库进行单元测试 - Unit test of generic repository using NUnit and NSubstitute 在单元测试中使用 DI 容器 - Using DI container in unit tests Nsubstitute-处理程序使用泛型时引发模拟对象的事件 - Nsubstitute - Raise event of mocked object when handler uses generics 如何使用 XUnit、Moq 和 AutoFixture 对控制器进行单元测试? - How to unit test a controller with XUnit, Moq and AutoFixture? 使用 NSubstitute 单元测试 Hangfire - Unit test Hangfire with NSubstitute
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM