Question

First of all I'm a beginner in unit tests. For my tests i want to use NSubstitute, so I read the tutorial on the website and also the mock comparison from Richard Banks. Both of them are testing against interfaces, not against classes. The statement is "Generally this [substituted] type will be an interface, but you can also substitute classes in cases of emergency."

Now I'm wondering about the purpose of testing against interfaces. Here is the example interface from the NSubstitute website (please note, that i have converted the C#-code in VB.net):

Public Interface ICalculator
    Function Add(a As Double, b As Double) As Double
    Property Mode As String
    Event PoweringUp As EventHandler
End Interface

And here is the unit test from the website (under the NUnit-Framework):

<Test>
Sub ReturnValue_For_Methods()

    Dim calculator = Substitute.For(Of ICalculator)()
    calculator.Add(1, 2).Returns(3)

    Assert.AreEqual(calculator.Add(1, 2), 3)

End Sub

Ok, that works and the unit test will perform successful. But what sense makes this? This do not test any code. The Add-Method could have any errors, which will not be detected when testing against interfaces - like this:

Public Class Calculator
    Implements ICalculator

    Public Function Add(a As Double, b As Double) As Double Implements ICalculator.Add
        Return 1 / 0
    End Function

    ...

End Class

The Add-Method performs a division by zero, so the unit test should fail - but because of testing against the interface ICalculator the test is successful.

Could you please help me to understand that? What sense makes it, not to test the code but the interface?

Thanks in advance Michael

Was it helpful?

Solution

The idea behind mocking is to isolate a class we are testing from its dependencies. So we don't mock the class we are testing, in this case Calculator, we mock an ICalculator when testing a class that uses an ICalculator.

A small example is when we want to test how something interacts with a database, but we don't want to use a real database for some quick tests. (Please excuse the C#.)

[Test]
public void SaveTodoItemToDatabase() {
  var substituteDb = Substitute.For<IDatabase>();
  var todoScreen = new TodoViewModel(substituteDb);

  todoScreen.Item = "Read StackOverflow";
  todoScreen.CurrentUser = "Anna";
  todoScreen.Save();

  substituteDb.Received().SaveTodo("Read StackOverflow", "Anna");
}

The idea here is we've separated the TodoViewModel from the details of saving to the database. We don't want to worry about configuring a database, or getting a connection string, or having data from previous test runs interfering with future tests runs. Testing with a real database can be very valuable, but in some cases we just want to test a smaller unit of functionality. Mocking is one way of doing this.

For the real app, we'll create a TodoViewModel with a real implementation of IDatabase, and provided that implementation follows the expected contract of the interface then we can have a reasonable expectation that it will work.

Hope this helps.


Update in response to comment

The test for TodoViewModel assumes the implementation of the IDatabase works so we can focus on that class' logic. This means we'll probably want a separate set of tests for implementations of IDatabase. Say we have a SqlServerDbimplementation, then we can have some tests (probably against a real database) that check it does what it promises. In those tests we'll no longer be mocking the database interface, because that's what we're testing.

Another thing we can do is have "contract tests" which we can apply to any IDatabase implementation. For example, we could have a test that says for any implementation, saving an item then loading it up again should return the same item. We can then run those tests against all implementations, SqlDb, InMemoryDb, FileDb etc. In this way we can state our assumptions about the dependencies we're mocking, then check that the actual implementations meet our assumptions.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top