【问题标题】:Moq + Autofixture: Using Setup for a dependant property clears the entire mock objectMoq + Autofixture:对依赖属性使用设置会清除整个模拟对象
【发布时间】:2021-03-01 16:14:40
【问题描述】:

我正在使用起订量和 AutoFixture。

给定以下接口:

public interface Int1
{
    Int2 Int2 { get; }
}

public interface Int2
{
    string Prop1 { get; }
    string Prop2 { get; }
}

我正在执行这样的测试:

using AutoFixture;
using AutoFixture.AutoMoq;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

[TestClass]
public class TestClass
{
    [TestMethod]
    public void Test1()
    {
        var f = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });

        var obj = f.Create<Mock<Int1>>();

        obj.Object.Int2.Prop1.Should().NotBeNullOrEmpty();
        obj.Object.Int2.Prop2.Should().NotBeNullOrEmpty();
    }

    [TestMethod]
    public void Test2()
    {
        var f = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });

        var obj = f.Create<Mock<Int1>>();

        obj.Setup(q => q.Int2.Prop1).Returns("test");

        obj.Object.Int2.Prop1.Should().Be("test");
        obj.Object.Int2.Prop2.Should().NotBeNullOrEmpty();
    }
}

第一个测试通过,第二个测试失败:Expected obj.Object.Int2.Prop2 not to be &lt;null&gt; or empty, but found &lt;null&gt;。似乎在 Int2 的依赖属性之一上使用 Setup 时,它会清除整个 Int2 对象(将所有属性设置为默认值)。这是为什么?如何避免?

obj.Object创建后的样子是这样的:

但是在执行Setup之后看起来像这样(Prop2null):

有趣的是,当我在创建 Int2 属性后访问它时,它工作正常。所以这个测试通过了(变量int2 没有在任何地方使用):

    [TestMethod]
    public void Test2()
    {
        var f = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });

        var obj = f.Create<Mock<Int1>>();

        var int2 = obj.Object.Int2;

        obj.Setup(q => q.Int2.Prop1).Returns("test");

        obj.Object.Int2.Prop1.Should().Be("test");
        obj.Object.Int2.Prop2.Should().NotBeNullOrEmpty();
    }

有什么想法吗?

这也是一个.csproj文件供参考:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net5.0</TargetFramework>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="AutoFixture" Version="4.15.0" />
        <PackageReference Include="AutoFixture.AutoMoq" Version="4.15.0" />
        <PackageReference Include="FluentAssertions" Version="5.10.3" />
        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
        <PackageReference Include="Moq" Version="4.16.1" />
        <PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
        <PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
    </ItemGroup>
</Project>

【问题讨论】:

    标签: c# unit-testing moq autofixture automoq


    【解决方案1】:

    简单地说,在您为Prop1 属性设置返回值后,您最终会为Int2 属性创建一个不同的模拟实例。为了满足您的要求,Moq 将生成一个新的模拟,它将返回 Prop1 的预期值。

    您可以将其视为等效于以下测试:

    [Fact]
    public void Test3()
    {
        var obj2 = new Mock<IInterfaceB>();
        obj2.Setup(x => x.Property1).Returns(Guid.NewGuid().ToString());
        obj2.Setup(x => x.Property2).Returns(Guid.NewGuid().ToString());
    
        var obj = new Mock<IInterfaceA>();
        obj.Setup(x => x.PropertyB).Returns(obj2.Object);
    
        obj.Setup(q => q.PropertyB.Property1).Returns("test");
    
        Assert.Equal("test", obj.Object.PropertyB.Property1);
        Assert.NotEmpty(obj.Object.PropertyB.Property2);
    }
    

    如果您想保留初始模拟实例并仅更改 Prop1,则可以使用 Mock.Get&lt;T&gt;(T)

    [Fact]
    public void Test4()
    {
        var f = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
    
        var obj = f.Create<Mock<IInterfaceA>>();
    
        var obj2 = Mock.Get(obj.Object.PropertyB);
        obj2.Setup(q => q.Property1).Returns("test");
    
        Assert.Equal("test", obj.Object.PropertyB.Property1);
        Assert.NotEmpty(obj.Object.PropertyB.Property2);
    }
    

    但我会推荐使用 AutoFixture 的 Freeze 功能

    [TestMethod]
    public void Test5()
    {
        var fixture = new Fixture()
            .Customize(new AutoMoqCustomization { ConfigureMembers = true });
    
        var int2Mock = fixture.Freeze<Mock<Int2>>();
        var int1Mock = fixture.Create<Mock<Int1>>();
    
        int2Mock.Setup(q => q.Prop1).Returns("test");
    
        int1Mock.Object.Int2.Prop1.Should().Be("test");
        int1Mock.Object.Int2.Prop2.Should().NotBeNullOrEmpty();
    }
    

    【讨论】:

      猜你喜欢
      • 2011-03-08
      • 2014-09-23
      • 2017-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-04
      相关资源
      最近更新 更多