Rhino Mocks: Recursion And Multiple Return Values From Stubs
A coworker and I were recently working on some recursive code in a WinForms app that followed these basic steps:
- Show a form
- If the return status was a certain value, show another form
- If the return status from the 2nd form was a certain value, repeat from Step 1
This creates a nice little recursion that allows a user to flip back and forth between two forms (a “Launch” view and a “Login” view). Here’s a simplified version of the code that we wrote:</p>
1: public void ShowLaunchView()
2: {
3: var result = AppController.RequestReply<ShowLaunchView, LaunchViewResult>();
4: if (result.Status == LaunchStatus.LoginRequested)
5: ShowLoginView();
6: //handle other status values here
7: }
8:
9: public void ShowLoginView()
10: {
11: var result = AppController.RequestReply<ShowLoginView, LoginViewResult>();
12: if (result.Status == LoginStatus.Cancelled)
13: ShowLaunchView();
14: //handle other status values here
15: }
</div> </div>
For those that are unfamiliar with my application controller, you can read more about that and the associated RequestReply method in previous posts.
Multiple Return Values From A Rhino Mocks Stub
In order to test the logic of this process correctly, we need to have the first form return a result of Ok and the the second form return a result of Ok, then the when the first form is shown again, it must return a result other than Ok. If we continue returning Ok over and over and over again, we’ll end up in an infinite loop and our test will crash.
To do this, we can use the the .Repeat statements in Rhino Mocks, to tell our stub how many times to return what value. Rhino Mocks will pay attention to the .Repeat calls and set up the stub to return the specified values, in the specified order, that many times. For example:
1: var myObject = MockRepository.GenerateMock<ISomeObject>();
2: myObject.Stub(m => m.DoSomething()).Return(someValue).Repeat.Once();
3: myObject.Stub(m => m.DoSomething()).Return(anotherValue);
</div> </div>
This code will set up the first call to myObject.DoSomething() so that it returns someValue. The second call will then return anotherValue. All calls after that will return the default value for the return type from the method. It’s important to note that if you do not specify the number of times to repeat for the first stub, then the second stub will simply overwrite the first one. The .Repeat.Once() call tells rhino mocks to create a sequence of stubs in the order that they are created.
Rhino mocks lets you string .Repeat calls together as many times as you need to, as well:
1: var myObject = MockRepository.GenerateMock<ISomeObject>();
2: myObject.Stub(m => m.DoSomething()).Return(someValue).Repeat.Once();
3: myObject.Stub(m => m.DoSomething()).Return(anotherValue).Repeat.Twice();
4: myObject.Stub(m => m.DoSomething()).Return(thirdValue).Repeat.Once();
5: myObject.Stub(m => m.DoSomething()).Return(fourthValue).Repeat.Times(5);
6: myObject.Stub(m => m.DoSomething()).Return(lastValue);
</div> </div>
This example would set up myObject for 10 calls. Ok, that may be a little extreme, but it illustrates the point. 🙂
Stubbing The Recursive Calls
Using the .Repeat syntax to set up the number of calls we expect, a simple unit test for the ShowLaunchView and ShowLoginView code may look like this:
1: [Test]
2: public when_cancelling_a_login_should_show_the_launch_view()
3: {
4: var appController = MockRepository.GenerateMock<IApplicationController>();
5:
6: //setup the two launch view calls that we expect
7: appController.Stub(ac => ac.RequestReply<ShowLaunchView, LaunchViewResult())
8: .Return(new LaunchViewResult(LaunchStatus.LoginRequested)).Repeat.Once();
9: appController.Stub(ac => ac.RequestReply<ShowLaunchView, LaunchViewResult())
10: .Return(new LaunchViewResult(LaunchStatus.Exit));
11:
12: //setup the one call to the login view we expect
13: appController.Stub(ac => ac.RequestReply<ShowLoginView, LoginViewResult>())
14: .Return(new LoginViewResult(LoginStatus.Cancelled));
15:
16: var SUT = new ApplicationStartupWorkflow(appController);
17: SUT.ShowLaunchView();
18: }
</div> </div>
Here is what happens in this test:
- App controller is told to show the launch view and return a launch view result. The first stub for the launch view call returns a result with a status of LoginRequested
- The conditional statement checks for LoginRequeted, finds it, and calls ShowLoginView
- App controller is told to show the login view and return a login view result. The only stub for the login view call returns a result with a status of Cancelled
- The conditional statement checks for Cancelled, finds it and calls ShowLaunchView
- App controller is told to show the launch view again and return another launch view result. This time, the second stub for the launch view call is used because the first one has already repeated for the number of times that we specified. The second stub returns a status of Exit which does not match the conditional, therefore the method exits and our test completes.
Much Nicer Than What I’ve Previously Done
This may be old news to those who have been using Rhino Mocks forever (… so what’s my excuse? I’ve been using RM for 3+ years now…) but it was a small learning moment for my coworker and I, last week. I’m happy to have learned this so that I can stop writing manual stubs for sequential return value needs. It is easier to maintain the rhino mocks generated stubs and keeps the test code cleaner, in my opinion.