Thursday, 8 December 2011

Understanding Mocks

When I first started using mocks, I must admit finding them slightly confusing. I knew what they were supposed to do but some of the language was a bit confusing so then I didn't know if I actually understood them at all. I wanted to write a very basic introduction to Mocks to get you in the mood for using them.
Firstly, a mock object (and I will be referring to Rhino mocks) is designed to act in place of an actual piece of software whether a service or even one of your own sub-systems. This is good for two reasons. Firstly, you might not have access to the real-life object (perhaps a web service that you cannot call from your test environment) but secondly, they allow you to test only a part of your system without risking the introduction of defects from other parts of the system (which might be outside of your control or not finished yet). For example, suppose you are writing a user interface to a banking system and you want to test your user interface. If you connect it to the actual back-end, you might see loads of errors that are nothing to do with your code and which make it hard to know whether your code is correct or not. By "mocking" the back-end, you can tell the mock how to behave and therefore test whether your code does what it says without worrying about someone else's defects.
To create a mock object you implement whatever interface you want to mock and then it is usual to encapsulate a generic mock property like so:

If your mock is for a service, you should also implement IDisposable and setup your end point replacement for web config files. The mock could use any of the service bindings, in this case I use a named pipe:

You would then need to implement any members of your interface and in most cases will simply forward the call onto the mock object

public void IInterfaceIWantToMock.SomeMethod()
{
mock.SomeMethod();
}

Now the bit that can seem quite tricky, the "expectations". The reason it is tricky is because although it is called an expectation, you do not have to "require" the call to be made and you can either be very specific about expectations or very general. It is better to be specific but in some cases this might mean lots of extra code. You can also specify things like how many times to expect the call to be made and what to return when the method is called in this way. It is probably neater to specify expectations inside the Mock class itself but you could expose the mock in a property and set them up elsewhere (just a choice of how tidy you want it). Let us specify a very simple expectation on our mock:

When this method is called, it tells the mock that if someone calls the mock method SomeMethod, you should allow it once but if it was called a second time, the mock would throw a null reference exception. You can specify a specific number of times or even Any() but don't be lazy with Any(), hopefully you will know how many times the method should be called. We will now look at a method which takes an argument and returns something to see what these look like. Bear in mind that lambda expressions are used extensively to make the syntax neater.

If we dissect this, we need to remember that this method is NOT what is going to be called during our testing, that would be the method called SomeOtherMethod(MethodRequest req). This is merely saying that when we do call that method with the id given in the call to the expect method, to return a response which includes a flag called success. This time we have said this will be called twice. We have specified that the arguments to the mock are important by matching id with the subsequent call to SomeOtherMethod() (we could setup the expectation to not care about some or all of the arguments but again that is in danger of being lazy). One of the things that this implies is that we could call this expectation method more than once with different Guids and the mock would then expect SomeOtherMethod() to be called twice for each different Guid passed in. This allows us to be very specific about what we expect and what we don't expect. Suppose for instance we are dealing with 2 different Guids in a test, we want to make sure the correct one is passed to a certain mock method, therefore we ensure that the call to the mock matches the parameter to one we told it to expect (like we did about in ExpectSomeOtherMethod()).
There are of course two sides to this story, one is that we want to tell the mock what we expect to pass to the mock but also we must specify what we would then expect the mock to return. This can cause us a risk because we might have to assume what the real-life object will return and we might get it wrong. Therefore, ideally, we would have input from the people who are writing the real object to ensure our mock expectations (specifically what they return for our arguments) are correct.
The consumer of the mock then has to create the mock object, setup expectations, optionally verify that all expectations were met and then dispose the mock if required. If using something like NUnit, it is easiest to setup, reset and dispose the mocks in the [Setup], [TearDown], [TestFixtureSetup] and [TestFixtureTearDown] methods to ensure they are always reset correctly (otherwise you might have a previous expectation hanging over from a previous test). An example of doing it all in a single method is shown below:

The assumption in this test is that DoSomethingWhichWillCallOntoTheMock() will do whatever it does including calling onto our mocked service. From our examples, it will call SomeMethod() once and SomeOtherMethod() twice with the given id. The call to VerifyAllExpectations is optional and when called will ensure that every single expectation was received. This would fail if you only called SomeOtherMethod() once for example or if you called it with the wrong arguments and it therefore hit a different expectation.
Hopefully you get the idea now...

Followers

About Me

I work for PixelPin being in charge of all development for our company, which includes mostly .Net web applications but also PHP, Android and iOS programming as well as managing our hardware and cloud-based systems.

I live in Cheltenham, Gloucestershire in the UK which is lovely in the summer and miserable in the winter.