Rhino Mocks 3.5 introduce some new concepts, mostly by building on top of the language features of C# 3.0.
User on the 2.0 platform needs not to worry, there are enough goodies for them as well (inline constraints, for example), but the focus of this document is on documenting the use of Rhino Mocks using C# 3.0.
EditWhat's New in Rhino Mocks 3.5
New things in Rhino Mocks 3.5:
- Arrange, Act, Assert model
- Lambda and C# 3.0 extensions
- Inline constraints
- Property Setters Explicit Expectation API
- Support for mocking interface in C++ that mix native and managed types.
- Allow a mock object to return to record mode without losing its expectations
- CreateMock was deprecated in favor of StrictMock
- Better error handling in edge cases.
- Fixed an issue with mocking internal classes and interfaces
- New event raising syntax
EditUsage Guidance
In general, I recommend following the "Test only one thing" per test. As such, the recommended approach for using Rhino Mocks (and mocking in general) is composed of a single mock object per test and several stubs (as needed).
EditThe difference between stubs and mocks
You can get the actual definition of the these terms in this article:
Mocks Aren't Stubs. I want to focus on the difference from the point of view of Rhino Mocks.
A mock is an object that we can set expectations on, and which will verify that the expected actions have indeed occurred.
A stub is an object that you use in order to pass to the code under test. You can setup expectations on it, so it would act in certain ways, but those expectations will never be verified. A stub's properties will automatically behave like normal properties, and you can't set expectations on them.
If you want to verify the behavior of the code under test, you will use a mock with the appropriate expectation, and verify that.
If you want just to pass a value that may need to act in a certain way, but isn't the focus of this test, you will use a stub.
IMPORTANT: A stub will never cause a test to fail.
Let us create a sample test, which will give us more context to talk about.
We want to write a test for sending an SMS for a forgotten password.
The code should:
- Get the user from the repository
- Reset the password to a random one
- Save the user with the new password
- Send an SMS with the new password
Note that I am using the new syntax here which will be explained shortly
First, we want to test that we reset the password, so we will write the following test:
public void When_user_forgot_password_should_reset_password()
{
var stubUserRepository = MockRepository.GenerateStub<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
stubUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
var controllerUnderTest = new LoginController(stubUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
Assert.AreNotEqual("this is not hashed password", theUser.HashedPassword);
}
In this test, we have created two stubs, passed them to the code under test, and asserted that the password was changed. To make this code pass we can use:
public void ForgotMyPassword(string username)
{
var user = users.GetUserByName(username);
user.HashedPassword = "new pass";
}
Now we want to verify that the user will save the password, so we have the following test:
public void When_user_forgot_password_should_save_user()
{
var mockUserRepository = MockRepository.GenerateMock<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
mockUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
mockUserRepository.Expect( x => x.Save(theUser) );
var controllerUnderTest = new LoginController(mockUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
mockUserRepository.VerifyAllExpectations();
}
Now we have used a mock object and set an expectations on it. Note that even though we are using a mock object here, we are still stubbing the call to GetUserByName. We don't care about this, since this is not what we are testing.
The code under test now looks like:
public void ForgotMyPassword(string username)
{
var user = users.GetUserByName(username);
user.HashedPassword = "new pass";
users.Save(user);
}
Another way to write the test would have been:
public void When_user_forgot_password_should_reset_password()
{
var stubUserRepository = MockRepository.GenerateStub<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
stubUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
var controllerUnderTest = new LoginController(stubUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
stubUserRepository.AssertWasCalled( x => x.Save(user));
}
We have no expectations in this test, just setting the required state and asserting on the actions that occurred when the code under test run.
The difference between the two test is subtle, but important. In most cases, you should prefer to use stubs. Only when you are testing complex interactions would I recommend to use mock objects.
If you need to ensure ordering, or expect to get a complex method, or want to ensure an exact adherence to the expectations.
Note that so far, we haven't even cared about the actual sending on an SMS, let us write a test that ensure that we can send an SMS.
public void When_user_forgot_password_should_sms_user()
{
var stubUserRepository = MockRepository.GenerateStub<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password", Phone = "1234-1234"};
stubUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
var controllerUnderTest = new LoginController(stubUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
stubbedSmsSender.AssertWasCalled( x => x.Send(
Arg.Is.Eqaul("1234-1234"),
Arg.Text.StartsWith("Password was changed to:")
));
}
Here we are asserting that the method was called, and use the inline constraint support to ensure we pass the correct arguments. The code under test now looks like:
public void ForgotMyPassword(string username)
{
var user = users.GetUserByName(username);
user.HashedPassword = "new pass";
users.Save(user);
smsSender.Send(user.Phone, "Password was changed to: new pass");
}
It is important to remember that we don't want to be tied down by our tests. We want to make sure that even if we modify the code under test, we will not break any unrelated tests.
EditCreateMock is deprecated, replaced by StrictMock. The use of Strict Mock is discouraged.
In Rhino Mocks 3.4 and previous, the "default" way to get a mock object was to call
mockRepository.CreateMock<ISmsSender>().
That caused a very serious issue.
CreateMock() would return a strict mock, and as we have already discussed, an overly strict mock will create a brittle test.
As a result of that,
CreateMock() was deprecated and will generate a compiler warning if used.
You can replace calls to
CreateMock() with
StrictMock().
However, using
StrictMock() is discourage. Strict mocks will fail if something that is not expected will happen to them. In the long run, this means that any change to the code under test can break your tests, even if the change has nothing to do with what you are actually testing in this specific test.
I encourage the use of stubs and dynamic mocks instead.
EditMocking with and without an instance of MockRepository
Rhino Mocks 3.5 introduce a new mode of mocking, using mocks without creating the repository.
The purpose of that is to reduce the amount of book keeping that you have to do.
Here is a test without the repository:
public void When_user_forgot_password_should_save_user()
{
var mockUserRepository = MockRepository.GenerateMock<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
mockUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
mockUserRepository.Expect( x => x.Save(theUser) );
var controllerUnderTest = new LoginController(mockUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
mockUserRepository.VerifyAllExpectations();
}
With a repository, the test would look like:
public void When_user_forgot_password_should_save_user()
{
var mocks = new MockRepository();
var mockUserRepository = mocks.DynamicMock<IUserRepository>();
var stubbedSmsSender = mocks.GenerateStub<ISmsSender>();
using(mocks.Record())
{
var theUser = new User{HashedPassword = "this is not hashed password"};
mockUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
mockUserRepository.Expect( x => x.Save(theUser) );
}
using(mocks.Playback())
{
var controllerUnderTest = new LoginController(mockUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
}
}
There are a few other differences between the two approaches:
- Mocks/stubs returned from
MockRepository.GenerateMock() and MockRepository.GenerateStub() are returned in replay mode, and do not require explicit move to replay mode. - Only Dynamic Mocks and Stubs are available without creating the repository. (This is done under the assumption that those are the most commonly used, and deserve their shortcut).
- Without creating a repository, you need to call VerifyAllExpectations() on each of the mock objects that you created. If you have a repository, you can simply call VerifyAll() on the repository and it will do the work for you. Since in most situations you will have only a single mock per test (and stubs require no verifications), that is not an issue.
EditArrange, Act, Assert
The major feature in Rhino Mocks 3.5 is the AAA syntax.
Arrange, Act, Assert is a classic way to setup your tests. First you arrange the state, then you execute the code under test, finally you assert that the expected state change happened.
With interaction based testing, this was generally impossible, and required that you would use the Record/Replay model.
This was a common case for confusion for people new to interaction based testing.
The AAA syntax solve this problem.
Let us take a look at a simple test and analyze it using the AAA approach:
public void When_user_forgot_password_should_reset_password()
{
// arrange
var stubUserRepository = MockRepository.GenerateStub<IUserRepository>();
var stubbedSmsSender = MockRepository.GenerateStub<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
stubUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
// act
var controllerUnderTest = new LoginController(stubUserRepository, stubbedSmsSender);
controllerUnderTest.ForgotMyPassword("ayende");
// assert
stubUserRepository.AssertWasCalled( x => x.Save(user));
}
We can easily see the different stages of the test.
The main supporters for this method of operation are the Expect() and Stub() methods.
EditExpect and Stub Extension Methods
The Expect() and Stub() methods are extension methods that become available for use on any mock object when you reference (
using in C# or
Imports in VB.NET) the Rhino.Mocks namespace.
EditExpect() Extension Method
The Expect() extension method creates a new expectation for this mock which must be verified/asserted later (using either the VerifyAllExpectations() or AssertWasCalled() methods).
Expect() functions similarly to the Expect.Call() functionality from previous versions of Rhino Mocks, except the .Expect() extension method can be used without a Record() block (whereas Expect.Call() must be called from within an explicit Record() block).
Consider the following example usage which ensures that the controller
SignOut action calls the
authSvc's SignOut method:
Test
public void should_sign_out_of_the_auth_service_when_the_logout_action_is_invoked()
{
IAuthService authSvc = MockRepository.GenerateMock();
authSvc.Expect(svc => svc.SignOut()).Return(true);
SignoutControllercontroller = new SignoutController(authSvc);
controller.SignOut();
authSvc.VerifyAllExpectations();
}
EditStub() Extension Method
The Stub() extension method will stub a specific member so that it will perform the default stub behavior (i.e. not fail the test --
for specific behavior, please see definition of stub earlier in this document) for that particular member.
You should use this behavior if you need to influence the result of the code in some fashion. Remember that by default Rhino Mocks will ignore unexpected methods calls. A good example for that is the following code:
public void SignOut()
{
if (authSvc.Signout())
otherDependency.UserIsLoggingOut();
}
Here is how we can test this. We stup the call to the SignOut and tell it to return true, then we expect a call to UserIsLoggingOut().
Test
public void should_notify_when_user_sign_out_successfully()
{
IAuthService authSvc = MockRepository.GenerateMock<IAuthService>();
INotificationService notificationSvc= MockRepository.GenerateMock<INotificationService>();
authSvc.Stub(svc => svc.SignOut()).Return(true);
notificationSvc.Expect((o => o.UserIsLoggingOut());
SignoutController controller = new SignoutController(authSvc, notificationSvc);
controller.SignOut();
notificationSvc.VerifyAllExpectations();
}
Another way to write this test is not to use explicit expectation, but to assert that a particular method call was called.
Test
public void should_notify_when_user_sign_out_successfully()
{
IAuthService authSvc = MockRepository.GenerateMock<IAuthService>();
INotificationService notificationSvc= MockRepository.GenerateMock<INotificationService>();
authSvc.Stub(svc => svc.SignOut()).Return(true);
SignoutController controller = new SignoutController(authSvc, notificationSvc);
controller.SignOut();
notificationSvc.AssertWasCalled(x=>x.UserIsLoggingOut());
}
EditWhat is the difference between GenerateStub and GenerateMock
GenerateMock
() is equivalent to calling DynamicMock() on a MockRepository instance in previous versions of Rhino Mocks. GenerateMock will create a dynamic mock (as opposed to a static mock or stub).
GenerateStub() is equivalent to calling Stub() on a MockRepository instance in previous versions of Rhino Mocks. GenerateStub will create a stubbed instance which will have stub behavior (see earlier in this article for the definition of Stub and explanation of Stub semantics).
EditUsing the AAA syntax in C# 2.0
Using the AAA syntax on C# 2.0 is possible, we just need to understand how the compiler manages to handle extension methods. Here is an example of the previous two tests using C# 2.0:
Test
public static void Test_Using_Extension_Methods_Using_2_0()
{
IAuthService authSvc = MockRepository.GenerateMock();
IUnrelatedDependency otherDependency = MockRepository.GenerateMock();
RhinoMocksExtensions.Expect(otherDependency, delegate(IUnrelatedDependency o)
{
o.UserIsLoggingOut();
});
RhinoMocksExtensions.Stub(authSvc, delegate(IAuthService svc)
{
svc.SignOut();
}).Return(true);
SignoutController controller = new SignoutController(authSvc, otherDependency);
controller.SignOut();
RhinoMocksExtensions.VerifyAllExpectations(otherDependency);
}
Test
public void Test_Using_AAA_Using_2_0()
{
IAuthService authSvc = MockRepository.GenerateMock();
IUnrelatedDependency otherDependency = MockRepository.GenerateMock();
RhinoMocksExtensions.Stub(authSvc, delegate(IAuthService svc)
{
svc.SignOut();
}).Return(true);
SignoutController controller = new SignoutController(authSvc, otherDependency);
controller.SignOut();
RhinoMocksExtensions.AssertWasCalled(otherDependency, delegate(IUnrelatedDependency x)
{
x.UserIsLoggingOut();
});
}
The syntax isn't as nice as what we get with C# 3.0, but we have all the functionality even for C# 2.0 :-)
EditArgument Constraints
Argument constraints belong to the definition of an expected call or stubbed call. Only if the argument constraints match, the specification of the call takes effect (this means: the expectation gets fulfilled, the return value gets returned, the Exception gets thrown, the Do gets executed and so on.) If the constraints do not match, it is as if another method has been called.
Argument constraints also define the method's signature by specifying the arguments types. That's why they can't be omitted. You can't define an expectation without specifying argument constraints, because you wouldn't specify the methods signature.
EditSimple Constraints
The simplest way to specify argument constraints is by directly using the value, as you see often in examples:
stubUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
The string value "ayende" is a constraint to the stub specification. Calls to GetUserByName with other arguments are not specified in this case.
stubUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
Assert.IsNull(stubUserRepository.GetUserByName("stefan"));
Assert.AreSame(theUser, stubUserRepository.GetUserByName("ayende"));
You can still specify that all argument values are accepted. To do this, use IgnoreArguments().
stubUserRepository.Stub(x => x.GetUserByName(null))
.IgnoreArguments()
.Return(theUser);
This simple 'directly-using-a-value' syntax is discouraged for various reasons. It is very limited. You can only define constraints that are compared using Equals, which is normally a reference comparison for reference types. You could also easily get problems because your value does not clearly specify a Type, For instance the null used in the example before does not specify a type. If the method has been overridden with arguments of different Types, you actually can't say which method you specify. (You could say for instance (string)null, which most users would not find intuitive.) Use inline constraints instead.
EditInline constraints
Note to users of previous versions of Rhino Mocks:
You're probably used to IgnoreArguments(), Constraints() and RefOut(). Rhino Mocks 3.5 introduces a new syntax for constraints, called the inline constraints, using Arg<T>. You can do everything with Arg<T> you could do before. It is encouraged to use only Arg<T>, it is more consistent and easier to understand, even if sometimes a little more to write.
Here is the previous example with inline constraints:
stubUserRepository.Stub(x => x.GetUserByName(Arg<string>.Is.Equal("ayende"))).Return(theUser);
Assert.IsNull(stubUserRepository.GetUserByName("stefan"));
Assert.AreSame(theUser, stubUserRepository.GetUserByName("ayende"));
For a first look, it might just look longer. You have to specify the type, which you might think is needless. Remember, you are specifying the methods signature at the same time.
You are not limited to an Equal comparison. See the constraints reference bellow.
Note: If you use inline constraints, all the method's arguments must be defined using inline constraints. You can't mix it with plain values.
EditIgnoring arguments
stubUserRepository.Stub(x => x.GetUserByName(Arg<string>.Is.Anything)).Return(theUser);
Assert.AreSame(theUser, stubUserRepository.GetUserByName("stefan"));
Assert.AreSame(theUser, stubUserRepository.GetUserByName("ayende"));
EditShortcut to Equal
There is also the shortcut Arg.Is<T>(T) which actually is the same as Arg<T>.Is.Equal(object). In .NET 3.5 you don't need to specify the generic argument, which results in this nice short syntax:
stubUserRepository.Stub(x => x.GetUserByName(Arg.Is("Ayende"))).Return(theUser);
EditProperties
You can easily specify constraints for values assigned to properties.
customerMock.Name = "Steinegger";
customerMock.AssertWasCalled(x => x.Name = Arg<string>.Is.NotNull);
Note, to expect that the property has been read, you need to assign the property to some variable inside an anonymous delegate.
string name = customerMock.Name;
customerMock.AssertWasCalled(x => {var ignored = x.Name;});
EditEvent registration
mock.Load += OnLoad;
mock.AssertWasCalled(x => x.Load = Arg<LoadEvent>.Is.Anything);
EditComplex expressions
Arg<T>.Matches allows you to define complex expressions. There are two overloads of this method. One allows you to define a lambda expression (.NET 3.5). The other allows you to define a complex expressions using classical Rhino Mocks Constraints.
// using lamda with .NET string evaluation (string.StartsWith)
stubUserRepository.Stub(x =>
x.GetUserByName(Arg<string>.Matches(y =>
y.StartsWith("aye", StringComparison.InvariantCulture)
|| y.StartsWith("stein", StringComparison.InvariantCulture)))
.Return(theUser);
Assert.AreSame(theUser, stubUserRepository.GetUserByName("steinegger"));
Assert.AreSame(theUser, stubUserRepository.GetUserByName("ayende"));
//using Rhino Constraints to build the expression
stubUserRepository.GetUserByName("ayende");
stubUserRepository.GetUserByName("steinegger");
stubUserRepository.Stub(x =>
x.GetUserByName(Arg<string>.Matches(Text.StartsWith("aye") || Text.StartsWith("stein")))
.Return(theUser);
Assert.AreSame(theUser, stubUserRepository.GetUserByName("steinegger"));
Assert.AreSame(theUser, stubUserRepository.GetUserByName("ayende"));
EditOut and Ref arguments
Ref and out arguments are special, because you also have to make the compiler happy. The keywords ref and out are mandantory, and you need a field as argument. Arg won't let you down:
User user;
if (stubUserRepository.TryGetValue("Ayende", out user))
{
//...
}
stubUserRepository.Stub(x =>
x.TryGetValue(
Arg.Is("Ayende"),
out Arg<User>.Out(new User()).Dummy))
.Return(true);
out is mandantory for the compiler.
Arg.Out(new User()) is the important part for us, it specifies that the out argument should return new User().
Dummy is just a field of the specified type User, to make the compiler happy.
It looks similar with ref arguments:
User user;
if (someMock.Foo(ref str))
{
//...
}
someMock.Stub(x =>
x.TryGetValue(ref Arg<string>.Ref(Is.Equal("Stefan"), "Oren").Dummy))
.Return(true);
Again, ref is mandantory.
Arg<string>.Ref(Is.Equal("Stefan"), "Oren") defines both the constraint and the return value. It uses "classical" Rhino Mocks Constraints.
Dummy is again the field fot the compiler.
EditConstraints Reference
| Arg<T>.Is |
| Equal(object), NotEqual(object) | Comparison using Equals |
| GreaterThan(object), LessThan(object), LessThanOrEqual(object), GreaterThanOrEqual(object) | Comparison using >, <, >= and <= operators |
| Same(object), NotSame(object) | Reference equality |
| Anything() | No constraints |
| Null(), NotNull() | Reference is null or not null |
| TypeOf(Type), TypeOf<T>() | Argument is of a certain type |
| Matching<T>(Predicate<T>) | Argument matches a predicate (.NET 3.5: see Arg<T>.Matches). Example: Arg<string>.Is.Matching(delegate(string s) { return s.Length == 2; } |
| IsIn(object) | Enumerable argument includes the specified object |
| Arg<T>.List |
| OneOf(IEnumerable) | Argument is in the specified list |
| Equal(IEnumerable) | All items in the enumerable argument are compared to the items in the specified list. |
| Count(AbstractConstraint) | Constraints to the Count property of the argument. |
| Element(int, AbstractConstraint) | Element at the specified index meets the constraint. |
| ContainsAll(IEnumerable) | The enumerable argument contains at least the specified items. |
| Arg<T>.Property |
| AllPropertiesMatch(object) | All the public properties and public fields are compared to the properties in the specified object. The comparesion is recusive if public properties or fields are complex types. |
| IsNotNull(string propertyName), IsNull(string propertyName) | Property of the given name is or is not null |
| Value(string propertyName, object expectedValue) | The property of the given name is equal to the expected value. |
| Value(string propertyName, AbstractConstraint constraint) | Property of the given Name meets the constraint. |
| Arg.Text |
| StartsWith(string), EndsWith(string), Contains(string) | String starts with, ends with or contains the specified text |
| Like(string regex) | Property matches to the regular expression |
| Arg<T>.Matches |
| Argt<T>.Matches(Expression) | Only in .NET 3.5 you can specify the constraint as a lambda expression, e.g. Arg<int>.Matches(x => x > 3) |
| Argt<T>.Matches(AbstractConstraint) | Specify Rhino Mocks Constraints, e.g. Arg<int>.Matches(Is.GreaterThan(0) && Is.LessThan(10)) |
EditHow to raise events
Rhino Mocks 3.5 introduces a new way of raising events from mocks. Prior to 3.5 this was generally done via the IEventRaiser interface like this:
[Test]
public void RaisingEventOnView()
{
IView view = mocks.CreateMock();
view.Load+=null;//create an expectation that someone will subscribe to this event
LastCall.IgnoreArguments();// we don't care who is subscribing
IEventRaiser raiseViewEvent = LastCall.GetEventRaiser();//get event raiser for the last event, in this case, View
mocks.ReplayAll();
Presenter p = new Presenter(view);
raiseViewEvent.Raise();
Assert.IsTrue(p.OnLoadCalled);
}
Rhino Mocks 3.5 provides some extension methods to make this a bit easier:
[Test]
public void RaisingEventOnViewUsingExtensionMethod()
{
IView view = mocks.CreateMock();
Presenter p = new Presenter(view);
view.Raise(x => x.Load += null, this, EventArgs.Empty);
Assert.IsTrue(p.OnLoadCalled);
}
The Raise() extension method takes a subscription to the event you want raised (view.Load in this case, as per the x => x.Load += null subscription), the event sender, and the arguments for the event.
EditProperty Setters Explicit Expectation API
Two constructs for expecting settings of properties have been added. It
has been possible with Rhino.Mocks before, however there has been no
expressive construct in the fluent interface syntax.
A property setting expectation with a certain value is specified like
this:
Expect.Call(mockedCustomer.Name).SetPropertyWithArguments("Ayende");
For testing the interaction only, a property setting expectation without
a specified value is done this way:
Expect.Call(mockedCustomer.Name).SetPropertyAndIgnoreArguments();
Those new constructs are equal to:
using(mocks.Record())
{
mockedCustomer.Name = "Ayende"; // setup an expectation for setting a specific argument.
LastCall.IgnoreArguments(); //We can ignore arguments to this expectation, with gives us expectation of setting the property,regardless of the actual value.
}
EditAccessing the method arguments directly
For some advance scenarios, it is easier to handle the call to the method directly, instead of setting constraints or asserting on particular values.
Rhino Mocks supports this feature using the WhenCalled method. Here is an example:
var wasCalled = false;
var stub = MockRepository.GenerateStub();
stub.Stub(x => x.StringArgString(Arg.Is("")))
.Return("blah")
.WhenCalled(delegate { wasCalled = true; });
Assert.AreEqual("blah", stub.StringArgString(""));
Assert.IsTrue(wasCalled);
This example only shows getting notified on the method call, but the feature is much more powerful. Take a look at the next example:
Test
public void Can_modify_return_value()
{
var stub = MockRepository.GenerateStub();
stub.Stub(x => x.StringArgString(Arg.Is("")))
.Return("blah")
.WhenCalled(invocation => invocation.ReturnValue = "arg");
Assert.AreEqual("arg", stub.StringArgString(""));
}
Test
public void Can_inspect_method_arguments()
{
var stub = MockRepository.GenerateStub();
stub.Stub(x => x.StringArgString(null))
.IgnoreArguments()
.Return("blah")
.WhenCalled(invocation => Assert.AreEqual("foo", invocation.Arguments0));
Assert.AreEqual("blah", stub.StringArgString("
}
As you can see, this is a powerful feature, so use with caution.