Agile Legacy Code

Primary Menu

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:

publicinterfaceIScanner

{

voidSetStream(FileStreamfileStream);

}

publicclassFileLoader

{

private readonly IScanner_scanner;

publicFileLoader(IScanner scanner)

{

_scanner = scanner;

}

publicvoidLoadStreamAndPosition(stringpath)

{

using (vars = newFileStream(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:

publicclassScannerSpy : IScanner

{

public FileStreamSpyStream { get; set; }

publicvoidSetStream(FileStreamstream)

{

SpyStream = stream;

}

}

[Test]

publicvoidTestDisposableOutput1()

{

// Arrange

varscannerSpy = newScannerSpy();

varloader = newFileLoader(scannerSpy);

// Act

loader.LoadStreamAndPosition(@&quot;c:\teststream.txt&quot;);

// 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.

publicclassScannerMock : IScanner

{

public longPosition { get; set; }

publicvoidSetStream(FileStreamstream)

{

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]

publicvoidTestDisposableOutput2()

{

// Arrange

varscannerSpy = newScannerMock();

scannerSpy.Position = 500;

// Act

varloader = newFileLoader(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

publicvoidLoadStreamAndPosition2(stringpath)

{

try

{

vars = newFileStream(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.

Author: Daniele Pozzobon

Daniele is an aspiring software craftsman with more that ten years of experience in the software industry. He is currently a consultant in the .Net space for a big insurance company, and previously have worked as a web dev in the manufacturing industry. He has experience with C#, Java, C++, PHP, Javascript, and lately has added some F# to the sauce.
He constantly annoys his friends by talking about software and is passionate about Agile methodologies, which gives him more opportunities to talk annoy his friends even more.
When there are no friends around to annoy, he blogs on CodeCleaners and in his free he time loves go hiking with his wife and two daughters.