Reputation: 2321
I'm new to nSubstitute. I saw code samples like this in many articles. What's use of testing the interface? When will the following test fail? Will the following test fail based on the actual implementation of IMathService? The idea here is to write this first in TDD and then remove the substitute and put the real implementation? Or the idea is to test some real implementation but substitute the dependencies with Nsubstitute? If yes, this example doesn't show that. I'm confused.
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);
}
Let's say I have the following implementation, will the above test fail? The line Substitute.For() will return the instance of MyMathService? I believe, it's not. If yes, what happens if I have more than one implementation for IMathService
class MyMathService: IMathService
{
public int Add(int a, int b)
{
throw new Exception("error");
}
}
Upvotes: 1
Views: 301
Reputation: 750
You are completely right, these snippets are not how you would typically use an isolation framework. These are just usage examples that show how to use the nSubsitute library. In your example, the test would pass because it's using a dynamically generated object that adheres to the IMathService interface but is programmed to return 4 when its Add method is called with the arguments 2 and 3. The real implementation isn't used anywhere in that test.
nSubstitute and its alternatives can be used for interaction testing of a class and its collaborators. You use these kinds of isolation frameworks to test classes in isolation. Typically you create a concrete instance of the System Under Test (SUT) but provide fake implementations for its collaborators. You can then set expectations on the fake collaborators or assert that certain methods were called on them to verify that the SUT interacts correctly with its collaborators. Instead of hand-rolling your own fake collaborators, you can use an isolation framework to automate this repetitive work for you.
An example hopefully makes things more clear. Let's say I'm working on class Foo and it's using a collaborator IBar that I have not yet implemented. IBar will talk to a real database so I don't want to use a real implementation of IBar in my isolated unit tests for Foo:
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();
}
Substitutes can be used in roughly two scenarios:
If you are familiar with the Arrange-Act-Assert definition for unit tests: Stubs are part of the Arrange phase of your test since they set up some dummy data that your SUT requires. Mocks are part of the Assert part since you use them to verify whether specific interactions occurred.
This technique is used heavily in a certain style of Test-Driven Development called outside-in TDD (also called London School TDD). It allows you to fully implement a class and specify the contracts for its collaborators without actually implementing the collaborators themselves, but just creating the interfaces. It can also be used in other scenarios, for example when you are unit testing and don't want to work with a real database, network traffic, a third party dependency and/or a class that is just terribly difficult or slow to use in tests.
For more information on these topics, check out Martin Fowler's post on Mocks Aren't Stubs.
Upvotes: 2