Unit Testing .NET with JustMock (tips and common pitfalls)
In this post, I’d like to share a few tips to get up and running with the Telerik mocking framework JustMock, so you can easily and quickly write tests that give you that warm-and-fuzzy feeling you crave from your code.
Notes
I assume some familiarity with the concepts of unit testing, dependency injection (DI), Inversion of Control (IoC), and mocking dependencies.
I’ll be referring to everything as a mock and not getting into the semantic differences between mocks and stubs and fakes and shims and so on, because I think that’s more confusing than productive.
You can add the JustMock nuget package to your test project to get started with lite, and optionally download and install the full version from Telerik.
Let’s begin!
Use a base class
Use a base class for your unit tests. I like to use a base class to set up any behavior I want shared among all tests, as well as bootstrapping our IoC container. We use a variety of IoC containers in our projects, but in this project I’ll be using Unity.
Setting up these containers ends up a being a lot of boiler plate in tests, so by moving them into a base class, I don’t have to duplicate code in test classes, or clutter them up with declarations. In my example, I use a shared UnityCache to register and resolve dependencies and JustMock to create the mock implementations:
public class TestsBase { private IDependency _dependency; public IDependency Dependency { get { if (_dependency == null) { _dependency = UnityCache.Resolve(); } return _dependency; } } public TestsBase() { UnityCache.Register(Mock.Create()); } }
Assert
Then my test class can inherit from the base and use the mock accordingly. For example, say we want to assert that by calling Program.SaveBytes, our bytes get saved to our dependency:
public class ProgramTests : TestsBase { [TestMethod] public void Program_DoStuffWithBytes_SavesData() { var bytes = new byte[8]; var program = new Program(); program.DoStuffWithBytes(bytes); Mock.Assert(() => Dependency.SaveData(bytes, Arg.AnyDateTime)); } }
We call the method under test and then call Mock.Assert with a lambda function to declare the method we are asserting on and with what parameters. This method asserts that Dependency.SaveData is called with the bytes we’re expecting. We’re testing that the correct bytes are saved in this method, so we don’t care what DateTime is passed in.
Aside on unit testing: By keeping only one assertion in the test, we can give the test a name that clearly states the intent of the test, there will be no question of what assertion is failing in the event of a failure, and the test can be written in no time at all. Write ten little tests to test ten things; do not write one giant test to test them all.
Know the default behavior
The default behavior of the mocked class will be to return default values for method calls that aren’t mocked. If you want more control on the test, or to be alerted when you forget to mock a dependency, you can instantiate the mock with strict behavior:
Mock.Create(Behavior.Strict);
This strict behavior would break the previous example, because Dependency.SaveData is called without first being arranged. You can decide which behavior you’d prefer, but it’s important to know the difference.
Arrange
To arrange a dependency to return a specific value is just as simple as Assert:
[TestMethod] public void Program_DoStuff_ReturnsCalculationIfTotalIsUnder10() { int total = 9, factor = 5, calculation = 50; Mock.Arrange(() => Dependency.GetCalculation(total, factor)) .Returns(calculation); var result = Program.DoStuff(total); Assert.AreEqual(calculation, result); }
Now Dependency.GetCalculation returns whatever we tell it. It’s also possible to have a more complicated return by using a lambda function:
[TestMethod] public void Program_DoStuff_ReturnsPrimaryCalculationIfTotalIsUnder10() { int total = 9, factor = 5; Mock.Arrange(() => Dependency.GetCalculation1(total, factor)) .Returns((int _total, int _factor) => { return _total + _factor; }); var result = Program.DoStuff(total); Assert.AreEqual(14, result); }
I would avoid adding logic into your unit test (unless you intend to unit test your unit tests), but there may be a situation where a return function is useful. Make sure the argument types match up with the method signature or you’ll get a runtime exception that may be confusing.
Use Internals
If your method is unwieldy and you don’t know where to start testing, it could be easier to break it into internal methods and test the internal methods individually. You’ll have to add an attribute in your project’s AssemblyInfo.cs to share the internals with your test project:
[assembly: InternalsVisibleTo("JustMockingAround.Tests")]
where JustMockingAround.Tests is the name of your test project assembly. After that, you test the internal methods as if they were any other public method. Then you can decide if you’re satisfied testing the internals or if you need to test the public method as well.
Go All-In With Full JustMock
Trying to set up mocks for a method that calls many internal methods that each call out to external dependencies, or other methods, can quickly turn into the stuff of nightmares. Treat yourself to the full version of JustMock and end the nightmares today. With the full version of JustMock, you can arrange your internal and static methods just like you would a public interface member.
You can even mock out things you don’t own, like objects in MSCorLib. Let’s revisit the first example, where we tested the bytes being saved. Before we didn’t care what date was being passed in, but now we want to know and test it. This is incredibly easy with JustMock:
[TestMethod] public void Program_DoStuffWithBytes_SavesWithUtcNowTimestamp() { var utcNow = DateTime.UtcNow; Mock.Arrange(() => DateTime.UtcNow).Returns(utcNow); Program.DoStuffWithBytes(default(byte[])); Mock.Assert(() => Dependency.SaveData(Arg.IsAny<byte[]>(), utcNow)); }
We don’t care what bytes were passed in, all we care is that it’s getting saved with UTC time at this very moment. So, we arrange DateTime.UtcNow to return a date we can keep track of and assert against. Now if some other person (or perhaps you) comes through your method and inadvertently changes DateTime.UtcNow to DateTime.Now, your test will break and alert you before any actual heartache.
Not convinced? What if you have a static Logger class that logs to a database and you don’t want to write logs all over the place for your tests. I like to arrange my logger to do nothing in the base class under my IoC container setup like so:
public ProgramTestsBase() { // IoC container stuff // IoC container stuff Mock.Arrange(() => Logger.Log(Arg.AnyString)).DoNothing(); }
Now I don’t care what was logged, my logger will do nothing. Of course, this doesn’t stop me from testing that my logger is getting called by a method:
[TestMethod] public void Program_DoStuffWithBytes_LogsByteSavingMessage() { Program.DoStuffWithBytes(default(byte[])); Mock.Assert(() => Logger.Log(“saved the bytes!”)); }
This can still be used to check the logger is getting called with the message I’m expecting without breaking my original call to do nothing.
That just about covers the most common uses of JustMock that I’ve used to improve my unit tests and life in general. For further reading on unit testing .NET, I highly recommend The Art of Unit Testing by Roy Osherove. If you’ve never written a unit test, or if you’ve written 5,000 unit tests, you can learn something from this book and take your testing to the next level.
For further reading on JustMock, check out the very thorough documentation provided by Telerik, it’s a great resource.
Want to play around with JustMock? Pull down this little project and get to it!