Introduction
Let me start off by saying that in general I don’t like the use of all kinds of weird frameworks in my unit-tests. They tend to make unit-tests more complex and more error prone than one might like in their unit-tests.
Rhino mocks however has proven to be a very valuable tool in scenario’s where I want to check if a particular component is showing the behavior I’m expecting. This is especially useful in cases where for example the flow inside a service controller is rather complex and encompasses many different flows.
In this article I’m going not going to show you how to work with rhino mocks. Instead I’m going to show how you can get more out of rhino mocks by using constraints in your expectations. However feel free to check out the documentation on how to get started.
Working with constraints
Building a test that uses basic parameter constraints on a method call is very straightforward. Actually, you will get it out of the box. Consider the following test case.
[TestMethod]
public void CalculateUsesValidSimpleInputReturnsComputedResult()
{
ICalculationManager calculationManager = MockRepository.GenerateMock<ICalculationManager>();
ServiceController subject = new ServiceController(calculationManager);
calculationManager.Expect(mgr => mgr.Calculate(10, 20)).Return(200);
subject.Calculate(10, 20);
calculationManager.VerifyAllExpectations();
}
This test case invokes the Calculate method with two parameters, both of type double. If I invoke the method using the wrong values for the parameters the VerifyAll method call on the manager mockup fails because the input values did not match the expected ones I put in the expectation earlier.
More constraints
While basic constraints work in some scenarios it often isn’t enough to get the testcase going. Sometimes you want to specify constraints like, parameter a should be less than 200. It is possible in rhino mocks to specify constraints for arguments by using the Arg<T> class. A sample of this is shown below.
[TestMethod]
public void CalculateUsesSimpleInputWithinExpectedRangeReturnsComputedResult()
{
ICalculationManager calculationManager = MockRepository.GenerateMock<ICalculationManager>();
ServiceController subject = new ServiceController(calculationManager);
// Specify the expectation including the constraints for the arguments
calculationManager.Expect(mgr => mgr.Calculate(
Arg<double>.Matches(arg => arg > 8 && arg < 12),
Arg<double>.Is.Equal(20.0))).Return(200);
subject.Calculate(10, 20);
calculationManager.VerifyAllExpectations();
}
It’s important to note two things here. First I used the Arg<T> class for both parameters. When working with this class you need to replace all parameter values with calls to the Arg<T> class. Also it’s important to know that there are two ways in which you can work with the Arg<T> class.
You can either work with a lambda expression to specify the contents of a constraint or use the methods on the Is property of the Arg<T> class.
Every constraint related to arguments can be specified inline in the method call. This is especially useful because that way the compiler will help you check the input for the method. If you specify the wrong arguments or the invalid number of arguments the test won’t compile.
Working with constraints on complex input types
Extending on the sample with explicit constraints on arguments you can imagine what you can do with complex input types. There are however a few things that make this last step harder than it seems.
There are a couple of options when working with complex input types. If you want to check that a specific reference is passed into the method you should either use Arg<T>.Is.Same(reference) or simply specify the variable containing the instance that needs to be passed in.This also works when you have overridden the Equals and GetHashCode methods on your complex type.
If you just want to make sure that a property on the input object is equal to a specific value; you should use somethings like the expression presented in the sample below.
[TestMethod]
public void CalculateUsesValidComplexInputReturnsComputedResult()
{
ICalculationManager calculationManager = MockRepository.GenerateMock<ICalculationManager>();
ServiceController subject = new ServiceController(calculationManager);
ComplexInputType complexInput = new ComplexInputType() { FirstValue = 10, SecondValue = 20 };
calculationManager.Expect(mgr => mgr.Calculate(Arg<ComplexInputType>.Matches(
x => x.FirstValue == 10 && x.SecondValue == 20))).Return(200);
subject.Calculate(complexInput);
// Verify that the manager was called
calculationManager.VerifyAllExpectations();
}
Conclusion
Rhino mocks presents a very strong mocking framework that offers quite a few options for developers to check the behavior of components in the application being developed.
I hope this article helped get a bit more perspective on the options with parameter constraints in rhino mocks.
Happy unit-testing!