The Rhino Mock Magic Of GetArgumentsForCallsMadeOn

One of the things that occasionally came in handy using Rhino Mocks previous to their AAA linq based syntax was the ability to see if operations on a mock were performed in a particular order. We could very easily set expectations inside and ‘Ordered’ block and if things happened out of order viola! you recieved an exception. Looked like the below:

       [Test]
        public void CompanySaveTest()
        {
            Company comp = new Company();
            comp.Name = "CompanyName";
            comp.Address = "Add1";
            comp.Phone = "222*111*3333";
            comp.User.Name = "Bill";

            int newCompID = 4;
            int newUserID = 2;

            MockRepository repo = new MockRepository();
            IDbManager dbMock = repo.DynamicMock<IDbManager>();

            using (repo.Ordered())
            {
                repo.Expect(dbMock.SaveComany(comp.Name, 0)).Return(newCompID);
                repo.Expect(dbMock.SaveUser(comp.User.Name, newCompID)).Return(newUserID);
                repo.Expect(dbMock.SaveComany(comp.Name, newUserID)).Return(newCompID);

            }
            
            //--Call Method Under Teset
            CompanyManger.SaveNewCompany(comp);

            repo.VerifyAllExpectations();

        }

When we switched to the AAA based syntax there was no way to reproduce the above, at least no obvious way. Luckily we did not require checking order to often, but when we did a co-op on my current project came up with a very effective alternative using the AAA syntax. The method only supports checking the order of operations called against a single mock object, but at least that’s something. Not the full ordered ability of the past, but I’ll take it when it’s handy. At least we can ensure the save calls on the company are in the right order.

The method ‘GetArgumentsForCallsMadeOn’ can be called on a mock after it has been used and it will return a list of object arrays. Each call on the mock will result in a new object[] in the list holding the parameters used in that call. More importantly the object arrays are added as each call on the mock is made, giving us a way to determine if calls were made on this mock in the appropriate order. As simple example looks like the below:

[Test]
        public void CompanySaveTest()
        {
            Company comp = new Company();
            comp.Name = "CompanyName";
            comp.Address = "Add1";
            comp.Phone = "222*111*3333";
            comp.User.Name = "Bill";

            int newCompID = 4;
            int newUserID = 2;

            MockRepository repo = new MockRepository();
            IDbManager dbMock = MockRepository.GenerateMock<IDbManager>();

            dbMock.Expect(db => db.SaveComany(comp.Name)).Repeat.Any.Return(newCompID);
           
            //--Call Method Under Teset
            CompanyManger.SaveNewCompany(comp);

            IList<object[]> args = dbMock.GetArgumentsForCallsMadeOn(db => db.SaveNewComany(Arg<string>.Is.Anything));

            //-- make sure called twice
            Assert.AreEqual(2, args[0].Length);
            
            //--check to make sure called in right order by checking updatingUserIDs of calls
            int updatingUserID = (int) args[0][1];
            Assert.AreEqual(0, updatingUserID);
            Assert.AreEqual(newUserID, updatingUserID);


        }

Now we can check order of calls on the mock object in question. Another nice thing is we can also do checks that were very cumbersume and not very readable in constraints and Arg<> checks in ‘AssertWasCalled’ methods in a much cleaner way.

//--check to make sure called in right order by checking updatingUserIDs of calls
            int updatingUserID = (int) args[0][1];
            Assert.AreEqual(0, updatingUserID);
            Assert.AreEqual(newUserID, updatingUserID);

            Company companySaved = args[0][0] as Company;
            //--check make sure arg is company
            Assert.IsNotNull(companySaved);

            //--now check what ever we like in cleaner way than constraint checking.
            Assert.AreEqual(newUserID, companySaved.LastModifiedUserID);

One thing to remember, however, is that when recording object calls on a mock the references are what it captures. When you do comparisons on reference arguments you are comparing the data as it is on the object at the end of the method. What this means is if you have an object ‘Customer’ and when passed to method ‘UpdateCustomer’ the ‘Name’ property is ‘Larry’ but the property is later changed to ‘Bill’, then the ‘Name’ property will reflect ‘Bill’, it’s state at the end of the method, whenever it is interrogated in an ‘AssertWasCalled’, ‘AssertWasNotCalled’ or ‘GetArgumentsForCallsMadeOn’ regardless of what the property value was when the method was actually called and recorded. This can be a pain when trying to do asserts, but such is life. In these cases you have to do the argument check on an expect previous to the mock being used.