I have been using NMock in my daily test driven development for over a year now. In that time, not much development has taken place. I mentioned in a previous entry that NMock lacks support for events. It also has a few other bugs that have been stacking up over the years. Yet the mailing list has had very little traffic.
Since I found NMock, several other .Net or C#mock object libraries have popped up. I have taken a good look at all of the free ones, and even though NMock has some problems, none of them really impressed me enough to switch away from NMock.
Earlier this week, I ran into a bug with NMock that without a fix, would have caused me to alter one of my designs in order to use it. NMock was unable to mock items in an interface’s parent interface. Traffic on the mailing list suggested that the bug had been fixed or worked around, yet no one had posted any details.
This discussion brought up the mention of a replacement for NMock that the developers had developed based on experiences with jMock. This replacement was checked into the NMock source tree at the end of June under the name NMock2.
When I hit this bug, I was experiencing crunch time here at work. I made request to the list for an NMock fix for the problem, but I really did not have time to wait arround for an answer. So I checked out NMock2 from the SourceForge CVS repository, and started reading the acceptance tests to see how to use it.
What I read really, really impressed me. The mock code that is produced when you use NMock2 reads very well.
I wrote a quick addition to the acceptance tests to see if my bug was there, and I was presented with the Glorious Green Bar of Success. I then added the binary to my project and used for the spots that that NMock did not work with.
Turns out that NMock2 had a different bug related to what I was doing, but this only took me a few minutes to fix. First, I wrote up a quick test that demonstrated the problem, and then I got to work. After a few minutes of tracing and debugging, I found the problem. NMock2 uses Type.GetMethod to get the methods that belong to a type. Even though the documentation suggests otherwise, Type.GetMethod does not get methods from an interface’s parent interfaces. Hmm… this sounds like the bug in NMock as well. My first test must have just sucked. I added a call to Type.GetInterfaces and then recursively called them to look for methods. This worked really well.
I never really dove into the NMock code all that much, but the code for NMock2 is really easy to follow. Furthermore, because of the test driven way that it was developed, I have a pretty high confidence that I did not break any existing functionality.
To make this entry even longer, I am going to share some snippets for code from both implementations that do the same thing. For both of these the following interface will be used to test the ComputeTotal method on a ShoppingCart object. (+10 points if you can figure out what that method does :))
public interface Item {
double Price { get; }
}
First is the old NMock way of doing things.
[Test]
public void ComputeTotal() {
// Create the mock object for item1 and have it return a price of 100
DynamicMock item1Mock = new DynamicMock(typeof(Item));
item1Mock.Strict = true;
item1Mock.ExpectAndReturn("Price", 100);
Item item1 = (Item) item1Mock.MockInstance;
// Create the mock object for item2 and have it return a price of 50
DynamicMock item2Mock = new DynamicMock(typeof(Item));
item2Mock.Strict = true;
item2Mock.ExpectAndReturn("Price", 50);
Item item2 = (Item) item2Mock.MockInstance;
// Add the items to the cart
ShoppingCart cart = new ShoppingCart();
cart.Add(item1);
cart.Add(item2);
// Get the total and make sure it is 150 (100 + 50)
int total = cart.ComputeTotal();
Assert.AreEqual(150, total);
// Verify that all the expects have been met
item1Mock.Verify();
item2Mock.Verify();
}
And now here is the NMock2 way.
[Test]
public void ComputeTotal() {
Mockery mock = new Mockery();
// Create the mock object for item1 and have it return a price of 100
Item item1 = (Item) mock.NewMock(typeof(Item));
Expect.Once.On(item1).GetProperty("Price").Will(Return.Value(100));
// Create the mock object for item2 and have it return a price of 50
Item item2 = (Item) mock.NewMock(typeof(Item));
Expect.Once.On(item2).GetProperty("Price").Will(Return.Value(50));
// Add the items to the cart
ShoppingCart cart = new ShoppingCart();
cart.Add(item1);
cart.Add(item2);
// Get the total and make sure it is 150 (100 + 50)
int total = cart.ComputeTotal();
Assert.AreEqual(150, total);
// Verify that all the expects have been met
mock.VerifyAllExpectationsHaveBeenMet();
}
Not only is the code shorter, but because of the way that it reads, the comments that I have added for clarity have almost become irrelevant.
I encourage you to go grab NMock2 and give it a shot. I think that it works really well, and I am in the process of replacing NMock with NMock2. It just reads too well not to. There are other pluses about NMock2 that I have not mentioned in this post, like support for events, but I will save that for another time.
I hope that you enjoyed this post, and that you learned something. For you non developers that read this, Wow. Kudos to you for reading this far! If you want, I will give you a special prize. :)
Thanks for the bug find in inherited interfaces.. has that been accounted for in the latest nmock build?
Umm… not that I know of, The mailing list has been very quiet. I can send you the NMock2 patch, if you are interested. The NMock2 developer, Nat Pryce, informed me that it will be applied as soon as he gets a chance.
Please send me this patch.. I want to start using NMock2.. I also can’t get access to CVS on sourceforge.net for the nmock2 folder.. I wouldn’t mind doing a bit of dev work on it.
i dont think you included the top level directory with solution, etc:
.cvsignore 1.1 2 months smgf first upload of nmock2
.project 1.1 2 months smgf first upload of nmock2
NMock2.nunit 1.1 2 months smgf first upload of nmock2
NMock2.sln 1.1 2 months smgf first upload of nmock2
README.TXT 1.1 2 months smgf first upload of nmock2
nant.build
NMock2 sample code had typo: item2 was typed as item1 in creating item2 section.
Well, shit. I should really learn to try to compile code before I post it. Thanks for pointing this out; I have fixed the code.
Could you please send me or post the patch you made to NMock2? I’m wanting to replace NMock with NMock2 in some of my current test suites and the inherited interfaces bug is biting me…
Thanks!
Joey
This bug has been fixed already, but my patch was not the one that was accepted. Someone beat me to it. You should be able to do a check out and be ready to go. There are still a few minor bugs that I have reported on the list, but you should not run into those. I still have a few patch that has not been committed yet that fixes one of the problems. You just reminded me to ask about it. :)
Thanks!
Hi! I want to start Test First development with NMock2, however, the cvs on sf.net looks like abandoned. Could you mind sharing the source with me?
Thanks for your help!
Hey Vaderpi,
It was interesting to know your experience with NMock and NMock2. Had you ever faced a problem with using enum in NMock, if you so did you happen to try it out with NMock2? Just curious to know.
Thanks for the information share.
–Vijay Patil
It has been a while since I used the old version of NMock, so I can’t remember. But for the newer version, I have never had any problems with enumerations. Good luck!
After using NMock2 for a while you will probably extract out a baseclass for your tests. You can find mine here… Search for Simplicity: MockingTestFixture makes NMock tests simpler
- Nigel Thorne