Rhino Mocks - Arrange, Act, Assert Syntax
I intend to release the new version of Rhino Mocks soon, and I wanted to show the new syntax that I have been working on. I still need to write more thorough documentation, but I think that just the syntax should be a pretty good indication of how things are going.
Without further ado, here is the code:
[Test] public void WhenUserForgetPasswordWillSendNotification_WithConstraints() { var userRepository = MockRepository.GenerateStub<IUserRepository>(); var notificationSender = MockRepository.GenerateStub<INotificationSender>(); userRepository.Stub(x => x.GetUserById(5)).Return(new User { Id = 5, Name = "ayende" }); new LoginController(userRepository, notificationSender).ForgotMyPassword(5); notificationSender.AssertWasCalled(x => x.Send(null), options => options.Constraints(Text.StartsWith("Changed"))); } [Test] public void WhenUserForgetPasswordWillSendNotification_WithArgMatchingInTheLambda() { var userRepository = MockRepository.GenerateStub<IUserRepository>(); var notificationSender = MockRepository.GenerateStub<INotificationSender>(); userRepository.Stub(x => x.GetUserById(5)).Return(new User { Id = 5, Name = "ayende" }); new LoginController(userRepository, notificationSender).ForgotMyPassword(5); notificationSender.AssertWasCalled(x => x.Send(Arg<string>.Matches(s => s.StartsWith("Changed")))); } [Test] public void WhenUserForgetPasswordWillSendNotification_WithArgumentMatching() { var userRepository = MockRepository.GenerateStub<IUserRepository>(); var notificationSender = MockRepository.GenerateStub<INotificationSender>(); userRepository.Stub(x => x.GetUserById(5)).Return(new User { Id = 5, Name = "ayende" }); new LoginController(userRepository, notificationSender).ForgotMyPassword(5); notificationSender.AssertWasCalled(x => x.Send("Changed password for ayende")); } [Test] public void WhenUserForgetPasswordWillSendNotification_UsingExpect() { var userRepository = MockRepository.GenerateStub<IUserRepository>(); var notificationSender = MockRepository.GenerateMock<INotificationSender>(); userRepository.Stub(x => x.GetUserById(5)).Return(new User { Id = 5, Name = "ayende" }); notificationSender.Expect(x => x.Send(null)).Constraints(Text.StartsWith("Changed")); new LoginController(userRepository, notificationSender).ForgotMyPassword(5); notificationSender.VerifyAllExpectations(); }
The class under test is:
public class LoginController { private readonly IUserRepository repository; private readonly INotificationSender sender; public LoginController(IUserRepository repository, INotificationSender sender) { this.repository = repository; this.sender = sender; } public void ForgotMyPassword(int userId) { User user = repository.GetUserById(userId); sender.Send("Changed password for " + user.Name); } }
Comments
When it comes to more advanced argument validation I find myself waffling back and forth between the pros and cons of the WithConstraints and WithArgMatchingInTheLambda options so it is nice to see that both options will (?) be available.
Everything else is looking very nice, as much as I still have yet to be convinced of the merit of act/arrange/assert over the classic set-expectations/exercise/assert(&verify) style, except perhaps for the simplicity of the very phrase "act/arrange/assert". :)
Ayende,
Awesome work. The AssertWasCalled looks to be a godsend for me. I've always wanted a way to verify just a single expectation at a time on a given mock. It would allow me to use a single set up and then many tests with only a single expectation check, versus when there are are two expectations on a mock and now you need to verify both on the mock. If there is another way now I would love to hear about it, for now I just look forward to your next release.
Good work!
For clarification, will both syntax options be available in the new release? The above syntax as well as the old legacy syntax, or will everything need to move over? I could see that causing some real issues for me as our project has a significant number of tests making sue of Rhino Mocks already.
John,
Yes, we are going to remain backward compatible with the previous versions.
I think this constraint syntax is finally viable. It always bothered me to have my constraints mixed in with my setup code. I want my assertions to be as clear as possible.
Looking good...though the name "GenerateStub" seems strange to me. Here's hoping we all simultaneously and spontaneously adapt Meszaros' test double nomenclature.
Will it be possible to have something like:
factory.StubAny((f) => f.CreateParameter()).Return(MockRepository.GenerateStub<DbParameter>());
The intent here would be to Stub Any call with this Return. Right now it looks like it is required to call Stub as many times as code under test will call it ...
Depends on what you are trying to achieve.
This would return the same instance for all calls.
This is probably what you are trying to get:
factory.Stub((f) =>f.CreateParameter()).Repeat.Any.Do( () => MockRepository.GenerateStub<DbParameter>());
Great syntax, I love it!
Is it possible to assert that a property setter was called using the new syntax?
mock.AssertWasCalled(x => x.Name = "blah");
Do with Action<T> would be nice to have, I'm trying the Beta but see only IMethodOptions<T> Do(Delegate action); which can't convert from lambda expression (and from anonymous delegate as well I guess?). God, it will be great to have lambda expressions everwhere.
How do I test events with this syntax. My old test case is as below (appliying Model View Presenter) Can you please guide me a little bit :
After some thought here is my implementation:
var mocks = new MockRepository();
I don't particularly like this:
userRepository.Stub(x => x.GetUserById(5)).Return(new User { Id = 5, Name = "ayende" })
Because it mixes together what could be considered an assertion (I expect them to ask for user ID 5) with the stubbing behavior (return this). As an alternative, I think I'd prefer:
userRepository.StubReturn(x => x.GetUserById, new User { Id = 5, Name = "ayende" })
And then add an assertion about things being called correctly to the bottom:
userRepository.AssertWasCalled(x => x.GetuserById(5))
Stub() doesn't set an expectation.
However, what I have found is that very often you want to do things like:
userRepository.Stub(x => x.GetUserById(5)).Return(new User { Id = 5, Name = "ayende" })
userRepository.Stub(x => x.GetUserById(6)).Return(new User { Id = 6, Name = "brad" })
This is just matching to parameters.
You can ask to not do that, but it turn out to be a rarer case in most scenarios.
@Ayende - I know this is now an old-ish post but thought it the best place to pose this question:
I've been trying out the AAA syntax in the last day and noticed that GenerateMock doesn't appear to produce strict mocks, nor can it take constructor arguments. On the other hand, instances returned from GenerateStub seem capable of taking on expectations, which given the now-looser semantics of GenerateMock would tempt me to suggest that the only difference between GenerateMock and GenerateStub should be that instances returned by GenerateStub shouldn't be able to take on any expectations. I've also noticed that there is no Generate* equivalent to PartialMock, though both GenerateStub and GenerateMock seem happy to produce one (not that I can tell the difference in expected behaviour due to the other things noted earlier.)
Could you perhaps post back a quick reply comment showing what maps to what between the between old the MockRepository instance methods and the new static GenerateMock and Generatestub methods? Thanks in advance!
Hehe. Nice copy/paste editing error on my part in that last comment. The actual question from me to Ayende should read:
Could you perhaps post back a quick reply comment showing what maps to what between the old MockRepository instance methods and the new static GenerateMock and GenerateStub methods?
The static methods are returning an object that can be used without record/replay. It is already in replay mode, which is needed to use the AAA syntax.
The missing partial is mainly an issue with not adding it, not a design decision.
I need to document that part fairly heavily
Yes, Ayende, I understand that the objects can be used without explicit record/replay, and have been using them that way quite successfully for most of the week. What I am curious about, though, is that the two methods are returning objects that don't seem to align with the previous terminology. The Mocks, for example, can be used as partial mocks, and more importantly have changed their strictness (they are acting much more loose than those returned from the classic StrictMock calls). I understand that you have yet to document this new syntax fully, but could you at least post a quick list of old call versus new call so that we can know what is what?
Yes, I'll try to do that.
Hi!
Thanks for the new 3.5 beta syntax, it is really nice to use.
However I have problems with mocking properties. E.g.:
var httpClient = MockRepository.GenerateStub<IHttpClient>();
httpClient.BaseAddress = "blah";
httpClient.AssertWasCalled(x => x.BaseAddress = "blah");
Will cause a ExpectationViolationException on the third row. And also trying to mock properties properties with:
var httpClient = MockRepository.GenerateStub<IHttpClient>();
httpClient.Stub(m => m.BaseAddress).Return("mocked");
or:
httpClient.Stub(m => m.BaseAddress = null).Throw(new ArgumentException("error"));
fails on the Stub rows with a "InvalidOperationException : Invalid call, the last call has been used or no call has been made ..."
I would appreciate if someone could give me a hint.
This should go to the mailing list
I really like it alot.
I have written about BDD and AAA syntax here:
http://morten.lyhr.dk/2008/07/doing-bdd-with-rhino-mocks-aaa-syntax.html
They really mix very well.
Comment preview