AutoFixture + NSubstitute + 虚拟方法
AutoFixture + NSubstitute + Virtual methods
想知道是否有办法使用 AutoFixture 和 NSubstitute 在具体 class 上模拟虚拟方法。我已经能够使用最小起订量轻松完成此操作,如下所示:
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");
}
}
如果您使用 AutoFixture 对 Parametrised Tests, here illustrated using 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,尽管您要求一个具体的 class,它应该通过 NSubstitute 创建它,以便您可以覆盖它可能拥有的任何虚拟成员.
AutoNSubstituteData
定义如下:
public class AutoNSubstituteDataAttribute : AutoDataAttribute
{
public AutoNSubstituteDataAttribute() :
base(() => new Fixture().Customize(new AutoNSubstituteCustomization()))
{
}
}
AutoDataAttribute
来自 AutoFixture.Xunit2, but if you prefer NUnit over xUnit.net, you should be able to use 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 实际上并没有做任何解决方法。
我可以想象真正的问题是在实际使用中,所讨论的具体 class 有您希望用数据填充的其他成员或构造函数数据。问题是由于 NSubstitute 的设计方式,我不知道有什么方法可以声明性地请求 'substitute';您将不得不使用 Substitute.For
方法,然后该方法会完全短路 AutoFixture 挂接到进程并添加其自身行为的能力。
对于 Moq,这是可能的,因为在 OP 中,您不是向 AutoFixture 询问 SomeConcreteClass
对象,而是向 Mock<SomeConcreteClass>
询问,这使得 AutoFixture 能够区分。
换句话说,Moq 遵循 Zen of Python,即 显式优于隐式, 这使得它的可扩展性达到了使用 NSubstitute 不容易实现的程度。出于这个原因,我一直认为最小起订量更好 API.
想知道是否有办法使用 AutoFixture 和 NSubstitute 在具体 class 上模拟虚拟方法。我已经能够使用最小起订量轻松完成此操作,如下所示:
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");
}
}
如果您使用 AutoFixture 对 Parametrised Tests, here illustrated using 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,尽管您要求一个具体的 class,它应该通过 NSubstitute 创建它,以便您可以覆盖它可能拥有的任何虚拟成员.
AutoNSubstituteData
定义如下:
public class AutoNSubstituteDataAttribute : AutoDataAttribute
{
public AutoNSubstituteDataAttribute() :
base(() => new Fixture().Customize(new AutoNSubstituteCustomization()))
{
}
}
AutoDataAttribute
来自 AutoFixture.Xunit2, but if you prefer NUnit over xUnit.net, you should be able to use 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 实际上并没有做任何解决方法。
我可以想象真正的问题是在实际使用中,所讨论的具体 class 有您希望用数据填充的其他成员或构造函数数据。问题是由于 NSubstitute 的设计方式,我不知道有什么方法可以声明性地请求 'substitute';您将不得不使用 Substitute.For
方法,然后该方法会完全短路 AutoFixture 挂接到进程并添加其自身行为的能力。
对于 Moq,这是可能的,因为在 OP 中,您不是向 AutoFixture 询问 SomeConcreteClass
对象,而是向 Mock<SomeConcreteClass>
询问,这使得 AutoFixture 能够区分。
换句话说,Moq 遵循 Zen of Python,即 显式优于隐式, 这使得它的可扩展性达到了使用 NSubstitute 不容易实现的程度。出于这个原因,我一直认为最小起订量更好 API.