c# - 使用 AutoFixture 中的 AutoMock 对构造函数中的响应式(Reactive)代码进行单元测试?

标签 c# unit-testing nunit system.reactive autofixture

在构造函数中设置响应式扩展代码的模拟时,我遇到了一些先有鸡还是先有蛋的问题。这是被测试的类(以及它所依赖的服务接口(interface)):

class MyViewModel
{
    public int Thing { get; set; }
    public MyViewModel(IMyService service)
    {
        service.StreamOfThings.Subscribe(x => Thing = x));
    }

    public void SomeClickEvent()
    {
        // Do something with `Thing`
    }
}
public interface IMyService
{
    IObservable<int> StreamOfThings { get; }
}

为了使测试更容易,我还定义了一个名为 AutoMockData 的自定义属性我可以使用 Moq 通过 AutoFixture 将模拟实例注入(inject)到我的类中:

public class AutoMockDataAttribute : AutoDataAttribute
{
    private static IFixture Create() =>
        new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
        
    public AutoMockDataAttribute() : base(Create) {}
}

有了这个,我就可以编写我的测试了(使用 NUnit3):

[Test, AutoMockData]
public void Verify_some_behavior(
    [Frozen] Mock<IMyService> mockService,
    MyViewModel vm)
{
    mockService.Setup(x => x.StreamOfThings).Returns(Observable.Return(100));
    vm.SomeClickEvent();
    vm.Thing.Should().Be(100);
}

此测试失败。我相信这会失败,因为 MyViewModel 的构造函数在 IObservable 的不同实例上设置可观察管道比我在单元测试方法中设置的那个要好。

这里理想的解决方案是使用 IObservable<>使用 AutoFixture 设置的实例,但我不确定如何最好地做到这一点。它以某种方式需要已经为我设置了一个预先构建的管道。

我发现的解决方法是不使用 AutoMock AutoFixture的功能并构造一个 Fixture直接直接使用.Freeze().Create()方法按正确的顺序排列。然而,这可能会导致单元测试主体更难以阅读且不太干净。

如何继续实现此处所示的测试,同时能够在 SUT 的构造函数中使用任何可观察量之前对其进行设置?

最佳答案

我认为这里的问题是你从未真正运用可观察的,所以 Thing保留 AutoFixture 在创建时分配给它的值。

在下面的示例中 Subject被卡住为 IObservable<int>然后将其解析为 StreamOfThings 的返回值。然后主题被迫触发订阅者。

[Test, AutoMockData]
public void Verify_some_behavior(
    [Frozen(Matching.ImplementedInterfaces)] Subject<int> observable, 
    MyViewModel vm)
{
    observable.OnNext(100);
    vm.SomeClickEvent();
    vm.Thing.Should().Be(100);
}

该示例等效于以下内容:

[Test, AutoMockData]
public void Verify_some_behavior(
    Subject<int> observable,
    [Frozen] Mock<IMyService> mockService,
    MyViewModel vm)
{
    mockService.Setup(x => x.StreamOfThings).Returns(observable);

    observable.OnNext(100);
    vm.SomeClickEvent();

    vm.Thing.Should().Be(100);
}

这种编写测试的方式还应该使如何处理多个可观察属性变得显而易见。

关于c# - 使用 AutoFixture 中的 AutoMock 对构造函数中的响应式(Reactive)代码进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68848578/

相关文章:

python - flask 单元测试 : how to test request from logged in user

c# - 传递 UInt32 作为参数 TestCase NUnit

c# - 不支持 NUnit 异步设置?

c# - 在 Unity 中仅将纹理应用于立方体的某些面

.net - 是否有任何代码覆盖工具可以让我按行排除代码?

c# - Web API 无法反序列化 Javascript Date.toISOString()?

php - 从方法不修改功能的装饰器调用方法的正确方法是什么?

unit-testing - 用于比较 NUnit 中的文本文件的单元测试

c# - 如何设置重复正则表达式?

c# - 如何将 css 添加到 asp.net web 应用程序