I just started a new project using Typemock Isolator. It's a new tool for me, and since I've mostly used Rhino Mocks, my learning is from the perspective of "how do you do this with Rhino Mocks". These are the differences I see so far.
- Typemock Isolator is a commercial product. Rhino Mocks is free and open source. Here's the Typemock pricing.
- Typemock Isolator can mock just about anything, including not normally mockable things like private methods, static methods, and sealed classes. Rhino Mocks has traditional mocking from an interface, abstract class, etc. There has been some hand-wringingabout whether or not Typemock was too powerful and encouraged untestable software designs. But I think it's uncommon to create a brand-new stand-alone project, at least in enterprise development. So you're probably dealing with at least some code that wasn't written with testing in mind. You can wrap those static classes and create new default constructors and all kinds of OO jujutsu. Or you can just leave it alone and mock it as-is with Typemock.
- With Typemock Isolator, everything starts with "Isolate" call. That's hard to beat for simplicity and discoverability. You can also use the AAA style syntax. Rhino Mocks also has AAA style testing in the newer versions. But there has been some confusion (at least in my mind) over the main entry points, like
MockRepository.GenerateStub<ISomeClass>(). Once you understand it, you're all set, but it's kind of a stumbling block for people new to Rhino Mocks.
OK, let's move on to other syntax differences. I grouped these by AAA style (arrange, act, assert). Since the "act" step should be invoking your system under test, the frameworks should look the same there. They differ in the setup syntax (arrange) and the verify syntax (assert).
For simple mock objects, Typemock uses this syntax:
// Arrange var repository = Isolate.Fake.Instance<IContactRepository>(); var contact = new Contact(repository); // Act contact.DoSomethingThatUsesARepository();
as compared to the Rhino Mocks syntax:
// Arrange var repository = MockRepository.GenerateStub<IContactRepository>(); var contact = new Contact(repository); // Act contact.DoSomethingThatUsesARepository();
Not much here. Just different ways to do the same thing.
Method call arguments are ignored by default in Typemock, so you don't have quite as much setup to get to the system under test. You still need to put the correct type in the argument, but it can be null, string.Empty, 0, etc.
With Rhino Mocks, you have to be explicit when arguments don't matter:
theClass.Expect(x => x.theMethod(null)).IgnoreArguments();
or you can use the newer, more readable but maybe more typing Rhino Mocks syntax:
theClass.Expect(x => x.theMethod(Arg<theArgument>.Is.Anything));
In Typemock, you use this syntax:
Isolate.WhenCalled(() => theClass.theMethod(theArgument)).WithExactArguments();
only if the arguments are important to you.
So you can ignore arguments or use specific arguments with either framework. They differ on the default setting.
In Typemock, you can also ignore calls on "live" methods (real instances, not fakes, stubs, or mocks) with:
Isolate.WhenCalled(() => theClass.theMethod(theArgument)).IgnoreCall();
The only way to not call into live code with Rhino Mocks that I'm aware of is to use a stub. This is the traditional testing approach that injects a stubbed dependency into the class under test.
So let's assume I need to test a Contact class method that does some stuff and then sends an email using EmailService, but I don't want an email to actually go out. Here's the Typemock arrange and act:
// Arrange var emailService = new EmailService(); var contact = new Contact(emailService); Isolate.WhenCalled(() => emailService.SendContactInfoToSales(contact)).IgnoreCall(); // Act contact.DoSomethingThatSendsAnEmail();
And the Rhino Mocks arrange and act:
// Arrange var emailService = MockRepository.GenerateStub<IEmailService>(); var contact = new Contact(emailService); // Act contact.DoSomethingThatSendsAnEmail();
For the Rhino Mocks approach, my EmailService has to be mockable. Again, you can write code to do that, and in this sample I am using an interface to EmailService to do that. But with Typemock, you just tell the test to ignore that call. You don't need to write more code to make the EmailService test-friendly.
Suppose EmailService is in some company-owned assembly you have to use and it's a sealed class. Yes, you can wrap it with an interface, but why not just tell it to skip email sending with Typemock. That's pretty powerful. You can focus on the new code you want to test instead of refactoring existing code you don't really care about but are dependent on.
Another cool Typemock thing you can do is force existing code to throw an exception so you can test the robustness of your code. What if I want to make sure my new code works OK if the mail relay is down on that legacy, sealed EmailService:
Isolate.WhenCalled(() => emailService.SendContactInfoToSales(contact)).WillThrow(new SmtpException());
You can do the same thing in Rhino Mocks. Again, just different syntax:
emailService.Expect(x => x.SendContactInfoToSales(contact)).Throw(new SmtpException());
I think you would still need to have a mock on an IEmailService to do this with Rhino Mocks. The Typemock advantage is that it can work around that sealed class.
The interaction testing assert syntax for Rhino Mocks is:
// Assert emailService.AssertWasCalled(x => x.SendContactInfoToSales(null), opt => opt.IgnoreArguments());
And for Typemock:
// Assert Isolate.Verify.WasCalledWithAnyArguments(() => emailService.SendContactInfoToSales(null));
This is just to test that the code got to that line/made that call internally. If you want to verify it got there and arrived with your expected arguments with Rhino Mocks:
// Assert emailService.AssertWasCalled(x => x.SendContactInfoToSales(expectedContact);
And with Typemock:
// Assert Isolate.Verify.WasCalled(() => emailService.SendContactInfoToSales(expectedContact));
So again, both frameworks can get you either assert type, just with different syntax.
The promise of Typemock Isolator is less time refactoring code that isn't on your roadmap and less setup to get to your system under test. I've focused here mostly on the arrange syntax for that reason.
Rhino Mocks can do most of the stuff Typemock can, except for the private, sealed, and static testing. Some would argue that you shouldn't test that code or if you need to make it testable, you should refactor it anyway.
Maybe. But it's also pretty appealing to keep your coding and tests focused on your project mandate, too.