So, a while ago I posted an introduction to Rhino Mocks. I thought it'd be good to add documentation for some more interesting features, and today I'm going to talk about constraints.
So, what is a constraint? If you remember from last time, I showed that you register expectations in Rhino Mocks (Oh, you don't know what Rhino Mocks is? See this link.) by executing method calls or property setters inside a using block:
using (_mocks.Record()) { /* some method or prop calls here */ }
When the actual code is executed Rhino Mocks checks whether the calls are done as specified. A constraint takes this concept a bit further, by making the "is this call as specified" check configurable. Lets start with an example. Let's say that we are in the same context as last time, with an order processing application of some sort, and that we have progressed into writing a Model-View-Presenter setup where the presenter should give the view the information to show. I'd like to know that the presenter actually assigns the specified order to the view:
[Test] public void ShouldBindWithNonNullOrder() { using (_mocks.Record()) { //make sure that the BindOrderInformation method //is called (with whichever argument) _view.BindOrderInformation(null); //now add a constraint on the last call, //saying it mustn't be null LastCall.Constraints(Constraints.Is.NotNull()); //We're not interested in this right now, //but we have to add it in order for the test to pass _view.BindOrderLines(null); LastCall.IgnoreArguments(); } using (_mocks.Playback()) { _presenter.Init(); } }
In this test, what is done is that I call the Init method on the presenter (which previously has been created with an order using Dependency Injection) and I then check that the presenter calls the view's BindOrderLines method with a non-null argument.
Now, the above most certainly is good to have, but say that there are some more interesting requirements that we need to fullfil. Say for instance that the presenter should initialize the view with all orderlines that have State != New. In the test context, I use an order with two orderlines, one of which have State = New, which means that we want the presenter to call BindOrderInformation with a list having a count of 1. Of course we could use the above code, removing the IgnoreArguments call and adding a NotNull constraint, but I'd love to be sure that we actually are binding the order information with exactly count 1. Enter the List constraint:
[Test] public void ShouldBindProcessedOrderInformationAndOrderLinesOnInitTake1() { using (_mocks.Record()) { //make sure that the BindOrderInformation method //is called (with whichever argument) _view.BindOrderInformation(null); //now add a constraint on the last call, //saying it mustn't be null LastCall.Constraints(Constraints.Is.NotNull()); //now, make sure that the presenter filters away those //orderlines having State != New _view.BindOrderLines(null); //take 1: we want to know the list count is 1 LastCall.Constraints( Constraints.List.Count(Constraints.Is.Equal(1)) ); } using (_mocks.Playback()) { _presenter.Init(); } }
This is superb, now I know the list count is 1. But say that I'd like to know that the correct OrderLine is filtered, then I don't know if it's the one with State = New or the other that's filtered away. So we need to take it one step further:
public void ShouldBindProcessedOrderInformationAndOrderLinesOnInitTake2() { using (_mocks.Record()) { //make sure that the BindOrderInformation method //is called (with whichever argument) _view.BindOrderInformation(null); //now add a constraint on the last call, //saying it mustn't be null LastCall.Constraints(Constraints.Is.NotNull()); //now, make sure that the presenter filters away those //orderlines having State != New _view.BindOrderLines(null); //take 2: we need to check that it's the correct OrderLine. //Let's do it in two steps, first create the //constraint to apply AbstractConstraint c = new Constraints.And( Constraints.List.IsIn(processedOrderLine), Constraints.List.Count(Constraints.Is.Equal(1)) ); //then apply the constraint LastCall.Constraints(c); } using (_mocks.Playback()) { _presenter.Init(); } }
Since I created the order myself in the SetUp-tagged method I know that it's the variable processedOrderLine that should be in the list, and I combine the IsIn constraint with the Count we applied previously. (Unfortunately there's no IsNotIn method, but since Rhino Mocks is an OSS project, it's more than possible to add it.)
On to next scenario. Let's say that I'd like to have a possibility to create a new Order, and that the view should be re-bound with it. I'd like to check that I really have a new Order, and I'd do something like this:
[Test] public void ShouldCreateNewAndBindViewWhenInvokingCreateNewAction() { using (_mocks.Record()) { _view.BindOrderInformation(null); // Using NHibernate, Id = 0 means that the record is unsaved. // (This is off course configurable.) LastCall.Constraints(Constraints.Property.Value("Id", 0)); // Make sure that the orderlines list is of count 0 _view.BindOrderLines(null); LastCall.Constraints(Constraints.List .Count(Constraints.Is.Equal(0))); } using (_mocks.Playback()) { _presenter.CreateNewAction(); } }
What's new here is that I use Constraints.Property.Value with the Property name (read via reflection).
So let's say that we, when we have created the OrderPresenter, have created it having the current Customer in the context, so that when we create a new Order, the current Customer automatically is attached. I'd do something like this to make sure that the customer on the new Order actually is the same as the one we have in our current context:
[Test] public void ShouldCreateNewAndBindViewWhenInvokingCreateNewActionTake2() { using (_mocks.Record()) { _view.BindOrderInformation(null); // Take 2: I want to check that the Id = 0, but I also want to check // that I have the current customer attached to the Order. // This is quite easy checked by looking up the Order's customer's // property called Name, and checking the property's value. AbstractConstraint c = new Constraints.And( Constraints.Property.Value("Id", 0), Constraints.Property.ValueConstraint("Customer", Constraints.Property.Value("Name", _customer.Name))); LastCall.Constraints(c); // Make sure that the orderlines list is of count 0 _view.BindOrderLines(null); LastCall.Constraints(Constraints.List .Count(Constraints.Is.Equal(0))); } using (_mocks.Playback()) { _presenter.CreateNewAction(); } }
The nice feature I use here is the Property.ValueConstraint that takes it one more step than the previous example; I check a property on the Order called Customer, and then apply a constraint on that object. In this case I apply the good old Value and check that the name actually is the same as the one that the _customer has. (Note that I'd normally check that the _customer are the same as the Order.Customer; but I had to come up with some example showing this feature, right?)
There, now I've shown some of the constraints that are possible to apply on calls to Rhino Mocks' mocks. I hope you'll have as much use of them as I have had during the last 8 months or so.
Oh, do look through the Rhino Mocks documentation for some more examples.
0 kommentarer:
Post a Comment