How To Test You Production Code: Using Mock Objects

By using Test Spies we can capture all the indirect outputs of our software to check that it is working correctly. Often times, making the assertions for all the outputs in the testing function leads to lot’s of duplicated code that after a while starts to hinder the purpose of the tests. Sometimes, we would like to do our assertions in the context of the running code and not in the context of the test suite.

Think for example that one of the indirect outputs of your SUT is a disposable object. How you verify that it is in the correct state when it get’s injected to your dependency if it gets destroyed as soon as the method you are testing exits?

Let’s try to understand this problem with an example: let’s say we want to verify that the method CreateAndConfigureDisposable passes the correct object to DependencyObject:


public interface IScanner
{
  void SetStream(FileStream fileStream);
}

public class FileLoader
{
  private readonly IScanner _scanner;

  public FileLoader(IScanner scanner)
  {
    _scanner = scanner;
  }
  public void LoadStreamAndPosition(string path)
  {
    using (var s = new FileStream(path, FileMode.Create))
    {
      s.Position = 500;
      _scanner.SetStream(s);
    }
  }
}

We could write a test method using a Test Spy and it will look like this:

public class ScannerSpy : IScanner
{
  public FileStream SpyStream { get; set; }
  public void SetStream(FileStream stream)
  {
    SpyStream = stream;
  }
}
[Test]
public void TestDisposableOutput1()
{
  // Arrange
  var scannerSpy = new ScannerSpy();
  var loader = new FileLoader(scannerSpy);
  // Act
  loader.LoadStreamAndPosition(@"c:\teststream.txt");
  // Assert
  Assert.AreEqual(500, scannerSpy.SpyStream.Position);
}

this test looks correct, we configure the SUT, we pass in the TestSpy, we exercise the method we want to test and the we do the assertions. The problem is that as soon as we exit the using block inside LoadStreamAndPosition the disposable object gets, well, disposed and trying to access its state will cause an ObjectDisposedException.

The best way to test this code is to do the assertions in the context of the running code that we want to test. This is where Mock Objects come into play: you put all the assertions inside the Test Double’s method that receives the indirect output.

public class ScannerMock : IScanner
{
  public long Position { get; set; }
  public void SetStream(FileStream stream)
  {
    Assert.AreEqual(Position, stream.Position);
  }
}

With this new Test Double the assertions run inside the using block while the DisposableObject still exists. By introducing the Mock Object the test logic flow changes quite a bit, the assertion step is missing and is substituted by a step for configuring the Test Double.

[Test]
public void TestDisposableOutput2()
{
    // Arrange
    var scannerSpy = new ScannerMock();
    scannerSpy.Position = 500;
    // Act
    var loader = new FileLoader(scannerSpy);
    loader.LoadStreamAndPosition(pathToTestStream);
}

So… what’s the big difference then between Spy Objects and Mock Objects

With a Mock Object you mock an object and with a Spy you spy on it 😉

Just kidding, I read a similar explanation while researching this topic.

The real explanation is that spies are used to collect information about the behavior of the code and then do all the assertions in the testing context while mocks may collect some information (like how many times a method gets called) but all the assertion are done in the context of the running code.

This distinction is important for a few reasons of which this are the two most important in my opinion:Some indirect outputs may not exists outside of the SUT context;

  • Some indirect outputs may not exists outside of the SUT context;
  • The code you are trying to test may swallow the exceptions generated by the assertions;

The first one is a case when you are forced to use a mock object because you need to do the assertions while the code is still running; we have already discussed this problem so let’s check the second one.

Let’s pretend that

public void LoadStreamAndPosition2(string path)
{
  try
  {
    var s = new FileStream(path, FileMode.CreateNew);
    s.Position = 500;
    _scanner.SetStream(s);
  }
  catch
  {
    //Gotta catch 'em all!
  }
}

In this case, we instantiate a non-disposable object that will live after the method LoadStreamAndPosition exists; We still could use a Mock Object if it wasn’t for the Pokémon Exception Handling that is going to catch the exceptions generated by the testing framework hiding all the failing assertions and making us think that the test run correctly.

Advertisements

One thought on “How To Test You Production Code: Using Mock Objects

  1. Pingback: How To Test Your Production Code: Using Fake Objects – Code Cleaners Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s