Previously on "Unit Testing in .NET", we’ve looked at:
- Introduction to Testing
- Your First Unit Tests
- Asserting That Your Code Rocks
- Overcoming Testing Hurdles
That means you should be comfortable with the all the fundamental concepts of software testing, and you should be equipped to deal with some common testing headaches. In this post, we’re going to look at how to simplify testing via dependency injection and the excellent mocking library, Moq.
What is mocking?
Mocking refers to the act of substituting a simulated object in place of a real object. In unit testing, it is quite common to use mocking to separate the class-under-test from its external dependencies, making the class much easier to fully test. I didn’t talk much about it, but that’s exactly what we were doing in the previous post when we applied dependency injection and the adapter pattern. We were substituting a fake object (the mock) that we had given a specific behavior to for the real object.
Sidetrack: what’s the difference between a mock object, a fake object, and a stub?
Nothing that really matters, in my opinion. Purists will argue the semantic differences between them, but I don’t think that’s really helpful as long as you use a mocking framework that is flexible enough to meet your testing needs.
Moq (I prefer the "mock" pronunciation just because it’s only one syllable instead of two) is a newer mocking framework for .NET that leverages many of the new platform and language features that arrived in .NET 3.5. This gives it an advantage over older mocking frameworks that were stuck using the features that were available in previous versions of .NET.
Getting started with Moq is easy. Just grab the DLL, add a reference to it, and start coding. Note that we will be using Moq 3.0 Beta, and that there are some major changes from 2.6 to 3.0, so the code below may need to be modified somewhat if you are using an older version.
Our First Test
Let’s revisit our LogMonitor from the previous post. Recall that this class takes in a log message, then dispatches an E-mail if the message contains the string "error". Originally, the class was tied directly to SmtpClient, but we applied the adapter pattern and dependency injection to simplify the tests. Here’s the original LogMonitor code:
And the LogMonitor tests:
Notice that we had to create a test class to serve as the mock object for LogMonitor. All is well. But wait, the requirements are about to change! Now we need to extend LogMonitor to handle warning messages, and warning and error messages need to go to two different accounts. Well, we *could* modify our test class to track the E-mails that were sent, then verify that the correct E-mail was sent for each log message we pass in, but then what’s going to happen when they want to add support for "fatal" log messages? Or super-critical-urgent messages? Our simple test class is going to become complicated in a hurry.
Enter Moq. Moq let’s us get rid of the test class for ISmtpClient completely. Instead, we’ll let Moq dynamically create a mock object for us. All we have to do is setup the expectations, the actions we expect the mock object to perform in response to certain method calls. In Moq 2.x, you did this using the Expects method, but in Moq 3.0, you use the new Setup method. For our first test, let’s verify that error messages are being sent to the new address by using Moq:
There are a few important things to call out here. First, we are creating a strict mock, which means that any calls to the mock which aren’t configured using the Setup method will result in exceptions. Note how we setup our expectations by specifying the exact values we expect for most of the parameters, but we use It.IsAny for the last parameter. That says "match any string value" for the last argument.
Second, the mock reference we create can’t be passed directly in as an ISmtpClient. You might think that Moq could provide an automatic cast or something, but the .NET framework just won’t allow it in the current version. So, to get to the actual mocked object, you access the Object property. Simply pass mock.Object in to LogMonitor just as you would any class that implements ISmtpClient.
Finally, notice that we have a call to Verify at the end. This will throw an exception if the exact call specified wasn’t executed. It is important to make sure that this matches the call you specified to Setup, otherwise your test will fail.
Go ahead and run the test. It should fail because we haven’t changed the address that error E-mails are being sent to. Do that now by editing LogMonitor, replacing "[email protected]" with "[email protected]". Re-run the test, and it should pass.
Next, let’s add a test for the warning messages. It looks almost identical to the test for errors:
Again, run the test to be sure that it fails before you start making changes to the class-under-test. Once you’re sure it fails, change the InspectLogMessage like so:
Run the test, and everything should pass!
We’ve only looked at a *very* trivial example so far, so you may be asking questions like "Can I mock a method that returns something?" and "Can I mock events and properties?" The answer to both these questions is "yes!" The Moq website has a very extensive Quick Start that illustrates the full power of Moq (note that it hasn’t been updated for Moq 3.0 yet, so translate Expect to Setup as you are reading the code). Let’s look at a few of the really cool features.
Automatically mock properties
You can easily stub-out simple properties using the Stub extension method, like so:
Try that example both with and without the call to Stub to see the difference. For more complicated properties, or to assign a default, use the overload that takes a second parameter:
You can also stub out all properties on the mock in a single call using StubAll.
Need to mock an entire object graph? Moq has you covered:
Every return value that can be mocked (meaning it is an interface or an unsealed class with overridable members) will be mocked. You can get the Mock wrapper around a mocked member via the Mock.Get static method. This is *very* handy when you are mocking complicated types (you know, like HttpContextBase in your ASP.NET MVC applications).
The Dangers of Mocking
Moq makes it very easy to create mocks, but you should be careful. Mocking is *not* a substitute for good design, and be careful not to write tests that depend on the internal behavior of the method you are testing. Remember that your tests should verify that a method does what it says it will do; it’s not meant to verify how the method is doing it. Here’s a great example of a bad test:
Notice how we’re verifying that the method is making a call to our mocked database. Well, what happens if we decide to implement some sort of caching scheme in our class-under-test? The method is still doing exactly what it says its going to do, but it will no longer make a call to our mocked database. If we added caching, we’d have to change our test even though the actual behavior of our method hasn’t changed (which is Bad).
Wrapping It Up
Well, I hope that was a useful crash-course on using Moq. For the next post in this series, I’m planning on screen-capturing a brief coding session of me doing test driven development. The only snag is that I’ve got to find a project that I can show (obviously my current employer isn’t too keen on sharing code that they’re paying for). Because of that, it may be a little while before you see part 6, but hopefully I’ll have some other topics to talk about Real Soon Now.