Thread safe version of Rhino Mocks

If you are a happy user of Rhino Mocks like I am, you might have experienced the same issue that I noticed this week at work.

We recently ported our entire code base to TFS, and decided to change the unit testing framework to MSTest so we could get a better integration with TFS.

We were a bit unhappy with the build times, so I started looking into running tests in parallel.

Luckily its very easy with MSTest, you simply add a parameter to your Local.testsettings file and you can run up to 5 tests in parallel, which is very nice, since most of us do in fact have multi core processors.

But when we changed the setting, we started seeing random test errors, one test run two tests were failing, another test run they were fine, and every sinle time you ran the test alone they worked perfectly. 

So we figured out it was caused by running multiple tests in the same test fixture in parallel, in conjunction with Rhino Mocks.

At first we thought it was MSTest that somehow was sharing state between tests in the same test fixture, but after a few test we found out that it was in fact Rhino Mocks that was not being built thread safe.

There were two issues, first there was a race condition that happens when you create a stub or mock.

Internally Rhino keeps a dictionary of Type and a proxy generator, so multiple calls to create the same type will have speed benefit. Unfortunately the code was not written in a thread safe way:

if (!generatorMap.ContainsKey(type))
    generatorMap[type] = new ProxyGenerator();

return generatorMap[type];

So what happens when two threads access this piece of code is that there is a high chance that they both will enter the body of the if statement and try to add the same key to the dictionary and thus fail. This is what we were seeing.

The generatorMap variable is a static variable in the class MockRepository, and as such thats fine, but since multiple threads can access it, there needs to be a guard in place to prevent both threads from trying to add the same key to the dictionary.

I created a small patch for this, not by adding a lock statement, but by simply adding a [ThreadStatic] attribute to the generatorMap variable. 

private static IDictionary<Type, ProxyGenerator> generatorMap;

For those that do not know what thread static means, let me explain it briefly. TheadStatic means that each thread will get its own instance of the variable, and in that way remove the race condition, since only one thread will access the variable at the same time. The variable will still be static, so several calls to MockRepository will still benefit from the map, but multiple threads will not muck things up for each other. 

One caveat with ThreadStatic is that each thread will have to initialize the member variable, otherwith it will only be the first thread that will get an instance that is not null, so I added a call in the constructor to instantiate the variable if it was not null.

Okay, one problem solved.

Another came up. 

Apparently there was another static variable causing issues. MockRepository holds a reference to the current Repository, which is bad, since it will prevent multi threading from working all together, since multiple threads will change the behaviour of each others mocks and cause tests to fail in the weirdest way. Luckily that was an easy fix, I just added a ThreadStatic to the variable and presto everything started working as expected.

internal static MockRepository lastRepository;

Luckily for you you do not have to do the same I just did. 

I downloaded the code from the source repository at git, and fixed the code.

I have put it up for download here. 

Just build the code using the instructions in the "How to build.txt" file, and you will get a nice Thread Safe Rhino Mocks dll. (9.93 mb)

Some might ask why I did not submit a patch to the author himself, and I did not do that because it seems like he have abandoned the project entirely. No code has been checked into the project in over a year.

Comments (5) -

Consider forking his code ( ) and adding your change.  Ayende may have stopped working on Rhino-Mocks but others continue to make improvements:

Just ran into this problem myself (I think), and downloaded your patched version of RM. It seems to have a problem though. All tests faill with a null object exception(from within the rhino codebase), when a mock is created (mockRepo.StrictMock<T>).

Hi Jasper,

I was not aware of that problem. I never use StrickMocks since it makes the tests very fragile in the sense that if someone adds a line somewhere in the code you are exersising, your strick mock will fail since something happened that was not configured.

I tend to use dynamic mocks, and set up expectations for only the stuff that I am interested in testing.

That being said, I will take a look at it.

I did however branch off the Rhino Mocks code, so I could continue development. - you can try to take a look at that.

I upgraded the Castle version, because the version being used had a bug. I also upgraded the unit testing framework.

You might not like that release, since I am trying to remove all the instance methods of MockRepository, so you are forced to use the static methods.

Could you send me an example of a test that fails - if you can reproduce it without giving me your entire code base, then I will make a test for it and see if I can fix the bug.

Fredrik Klintebäck 4/19/2012 11:56:45 AM

We frequently get the infamous "This action is invalid when the mock object is in replay state" exception from Rhino Mocks, even though we don't run tests i parallel, so I decided to give Mammock a try to see if that would help.

We use the service locator pattern instead of DI for IoC, and to save (development) time we just stub every service (using reflection to find all services) in each unit test. However, with Mammock running tests becomes painfully slow and after just a few tests (less than 20), an out of memory exception was thrown.

I guess (without any deeper analysis) that the problem is that the generator map now is thread static.

Anyway, I like the effort you are investing in Mammock. Looking forward to see how it evolves.

Hi Fredrik,

I will have a look and see what makes it fail.

Can you give me an example of a unit test that fail, I need to see what features you are using from the code.

Thank you


Pingbacks and trackbacks (2)+

Comments are closed