我是nSubstitute的新手。我在很多文章中都看过这样的代码示例。什么用于测试界面?以下测试何时失败?以下测试是否会因IMathService的实际实现而失败?这里的想法是首先在TDD中写这个,然后删除替代品并把真正的实现?或者想法是测试一些真正的实现,但用Nsubstitute替换依赖关系?如果是,这个例子没有显示出来。我很困惑。
public interface IMathService
{
int Add(int a, int b);
}
[Test]
public void nSubstituteMockingInterface()
{
var mathService = Substitute.For<IMathService>();
mathService.Add(2,3).Returns(4);
Assert.IsTrue(mathService.Add(2, 3) == 4);
}
让我说我有以下实施,上述测试会失败吗? Substitute.For()行会返回MyMathService的实例吗?我相信,它不是。如果是,如果我有多个IMathService实现
会发生什么class MyMathService: IMathService
{
public int Add(int a, int b)
{
throw new Exception("error");
}
}
答案 0 :(得分:2)
您完全正确,这些片段不是您通常使用隔离框架的方式。这些只是展示如何使用nSubsitute库的用法示例。在您的示例中,测试将通过,因为它使用的是动态生成的对象,该对象遵循IMathService接口,但编程为在使用参数2和3调用其Add方法时返回4.实际的实现不会在任何地方使用测试
nSubstitute及其替代品可用于类及其协作者的交互测试。您可以使用这些类型的隔离框架来单独测试类。通常,您可以创建受测系统(SUT)的具体实例,但为其协作者提供虚假实现。然后,您可以设置对假协作者的期望,或断言调用某些方法来验证SUT是否与其协作者正确交互。您可以使用隔离框架为您自动执行重复性工作,而不是手动滚动自己的假合作者。
一个例子希望能让事情变得更加清晰。假设我正在研究Foo类,它正在使用我还没有实现的合作者IBar。 IBar将与一个真实的数据库交谈,所以我不想在我的Foo隔离单元测试中使用真正的IBar实现:
public class Foo
{
private readonly IBar _bar;
public Foo(IBar bar)
{
_bar = bar;
}
public void DoSomething()
{
_bar.DoSomethingElse();
}
}
public interface IBar
{
void DoSomethingElse();
}
public class AnImplementationOfBar : IBar
{
public void DoSomethingElse()
{
throw new System.NotImplementedException();
}
}
[Test]
public void Foo_interacts_correctly_with_its_bar()
{
var fakeBar = Substitute.For<IBar>();
var foo = new Foo(fakeBar);
foo.DoSomething();
fakeBar.Received().DoSomethingElse(); //This is the assertion of the test and verifies whether fakeBar.DoSomethingElse has been called.
}
public static void Main(string[] args)
{
//Production code would look like this:
var foo = new Foo(new AnImplementationOfBar());
//next line throws because AnImplementationOfBar.DoSomethingElse has not been implemented yet
foo.DoSomething();
}
替代品可以在大致两种情况下使用:
如果您熟悉单元测试的Arrange-Act-Assert定义:Stub是测试的 Arrange 阶段的一部分,因为它们设置了您的SUT所需的一些虚拟数据。模拟是断言部分的一部分,因为您使用它们来验证是否发生了特定的交互。
这种技术在某种称为外向TDD(也称为伦敦学校TDD)的测试驱动开发中大量使用。它允许您完全实现一个类并为其协作者指定合同,而无需实际实现协作者本身,而只是创建接口。它也可以在其他场景中使用,例如,当您进行单元测试并且不想使用真实数据库,网络流量,第三方依赖关系和/或一个非常困难或使用缓慢的类时测试
有关这些主题的更多信息,请查看Martin Fowler在Mocks Aren't Stubs上的帖子。