【问题标题】:AutoFixture + NSubstitute + Virtual methodsAutoFixture + NSubstitute + 虚拟方法
【发布时间】:2018-02-01 23:45:32
【问题描述】:

想知道是否有一种方法可以使用 AutoFixture 和 NSubstitute 在具体类上模拟虚拟方法。我已经能够使用 Moq 轻松做到这一点,如下所示:

public class SomeConcreteClass
{
    public string MethodA()
    {
        return MethodB();
    }

    public virtual string MethodB()
    {
        return "AAA";
    }
}

[TestFixture]
public class SomeConcreteClassTests
{
    private IFixture _fixture;
    private SomeConcreteClass _someConcreteClass;

    [SetUp]
    protected void Setup()
    {
        _fixture = new Fixture()
            .Customize(new AutoMoqCustomization());
        var someConcreteClassMock = _fixture.Create<Mock<SomeConcreteClass>>();
        _someConcreteClass = someConcreteClassMock.Object;
        someConcreteClassMock.CallBase = true;
    }

    [Test]
    public void SomeScenario()
    {
        Mock.Get(_someConcreteClass).Setup(m => m.MethodB()).Returns("BBB");
        var actual = _someConcreteClass.MethodA();
        actual.ShouldBe("BBB");
    }
}

【问题讨论】:

    标签: unit-testing autofixture nsubstitute


    【解决方案1】:

    如果您使用 AutoFixture 对 Parametrised Tests 的支持,这是最好的实现,这里使用 xUnit.net 进行说明(但是,IIRC,对 NUnit 也有类似的支持):

    [Theory, AutoNSubstituteData]
    public void ImplicitSubtituteViaAttribute([Substitute]SomeConcreteClass scc)
    {
        scc.MethodB().Returns("BBB");
        var actual = scc.MethodB();
        Assert.Equal("BBB", actual);
    }
    

    使用[Substitute] 属性可以让您明确告诉 AutoFixture,尽管您要求一个具体的类,但它应该通过 NSubstitute 创建它,以便您可以覆盖它可能拥有的任何虚拟成员。

    AutoNSubstituteData 是这样定义的:

    public class AutoNSubstituteDataAttribute : AutoDataAttribute
    {
        public AutoNSubstituteDataAttribute() :
            base(() => new Fixture().Customize(new AutoNSubstituteCustomization()))
        {
        }
    }
    

    AutoDataAttribute 来自 AutoFixture.Xunit2,但如果您更喜欢 NUnit 而不是 xUnit.net,您应该可以使用 AutoFixture.NUnit3 代替。


    否则,我不确定您能否获得与使用 AutoFixture.AutoMoq 完全相同的结果。在这个退化的例子中,你可以这样做:

    [Fact]
    public void ImperativeWorkaround()
    {
        var fixture = new Fixture().Customize(new AutoNSubstituteCustomization());
        fixture.Register(() => Substitute.For<SomeConcreteClass>());
        var scc = fixture.Create<SomeConcreteClass>();
        scc.MethodB().Returns("BBB");
    
        var actual = scc.MethodB();
    
        Assert.Equal("BBB", actual);
    }
    

    然而,这是毫无意义的,因为你也可以这样写:

    [Fact]
    public void Reduction()
    {
        var scc = Substitute.For<SomeConcreteClass>();
        scc.MethodB().Returns("BBB");
    
        var actual = scc.MethodB();
    
        Assert.Equal("BBB", actual);
    }
    

    换句话说,AutoFixture 实际上并没有在该解决方法中做任何事情。

    我可以想象真正的问题是,在实际使用中,有问题的具体类具有您希望填充数据的其他成员或构造函数数据。问题是,由于 NSubstitute 的设计方式,我不知道您可以以任何方式声明性地要求“替代品”;您将不得不使用Substitute.For 方法,这会完全破坏 AutoFixture 挂钩进程并添加自己的行为的能力。

    使用 Moq,这是可能的,因为在 OP 中,您不是向 AutoFixture 询问 SomeConcreteClass 对象,而是询问 Mock&lt;SomeConcreteClass&gt;,这使 AutoFixture 能够区分。

    换句话说,Moq 遵循Zen of Python,即显式优于隐式,,这使得它的可扩展性达到了使用 NSubstitute 不容易实现的程度。出于这个原因,我一直认为 Moq 拥有更好的 API。

    【讨论】:

    • 非常感谢马克,我真的很感激。是的,我喜欢 AutoFixture 让我不必担心构造函数需要什么参数或其顺序。顺便说一句,你的超级粉丝,很喜欢你在 Pluralsight 上的演讲。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多