Catching Arguments with Rhino Mocks

I’ve noticed that in quite a few of my tests, I was using a mock simply to test one argument on one of the methods called.  Maybe I was making sure it passed a correctly formatted string, the right date/time, or whatever.  There was a lot of setup and overhead (even using the new AAA syntax in Rhino Mocks) just to catch one argument.  While pairing with Josh Flanagan, he said, “Wouldn’t it be cool if you could just catch the argument rather than having to go through all the mock setup stuff? I think Jeffrey Palermo did something like this.”  Sure enough, he was right.  We found this post which was almost exactly what we wanted. It was a little behind the times, however, since it didn’t account for some .NET 3.5 features (namely lambda expressions and expression trees) nor the new AAA syntax.  We decided to take a few minutes and update it and this is what we came up with!

CapturingConstraint

First, we decided to use the same approach Jeffrey did by using the Constraint functionality in Rhino Mocks and came up with this:

 

   1: public class CapturingConstraint : AbstractConstraint{
   2:     private readonly ArrayList argList = new ArrayList();
   3:  
   4:     public override bool Eval(object obj)
   5:     {
   6:         argList.Add(obj);
   7:         return true;
   8:     }
   9:  
  10:     public T First<T>(){
  11:         return ArgumentAt<T>(0);
  12:     }
  13:  
  14:     public T ArgumentAt<T>(int pos){
  15:         return (T) argList[pos];
  16:     }
  17:  
  18:     public override string Message{
  19:         get { return ""; }
  20:     }
  21:  
  22:     public T Second<T>(){
  23:         return ArgumentAt<T>(1);
  24:     }
  25: }

 

This class captures everything Rhino Mocks throws at it. You can access the parameters, in order, by using the ArgumentAt method. It’ll also do the casing for you using a Generic type.  We also added convenience methods for the first and second arguments since 99% of the time that’s all we cared about.

CaptureArgumentsFor Extension Method

The next thing we did is to add an extension method to System.Object to allow you to specify which method’s arguments you wish to capture.  Unfortunately we had to add an extension method to System.Object which is usually not a good thing to do, but our mocks could be of any type, so we couldn’t get more specific than that.  Also, this extension method is only available in our unit testing code, so this crime again Extension Methods is fairly localized.

   1: public static CapturingConstraint CaptureArgumentsFor<MOCK>(this MOCK mock, Expression<Action<MOCK>> methodExpression)
   2: {
   3:     var method = ReflectionHelper.GetMethod(methodExpression);
   4:  
   5:     var constraint = new CapturingConstraint();
   6:     var constraints = new List<AbstractConstraint>();
   7:  
   8:     foreach( var arg in method.GetParameters())
   9:     {
  10:         constraints.Add(constraint);
  11:     }
  12:  
  13:     mock.Expect(methodExpression.Compile()).Constraints(constraints.ToArray());
  14:  
  15:     return constraint;
  16: }

Usage Example

Finally, our test ends up a bit more simple and looks something like this:

   1: [Test]
   2: public void should_correctly_assemble_the_notification_batch_with_the_context_and_template_group()
   3: {
   4:     Services.PartialMockTheClassUnderTest();
   5:     
   6:     var argCatcher = ClassUnderTest.CaptureArgumentsFor(a => a.ExecuteBatch(null, null));
   7:  
   8:     ClassUnderTest.Execute(_context);
   9:  
  10:     var batch = argCatcher.Second<UserMessageBatch>();
  11:     
  12:     batch.Resolver.ShouldBeTheSameAs(_context);
  13:     batch.Group.ShouldBeTheSameAs(ClassUnderTest.TemplateGroup);
  14: }

Related Articles:

    Post Footer automatically generated by Add Post Footer Plugin for wordpress.

    About Chad Myers

    Chad Myers is the Director of Development for Dovetail Software, in Austin, TX, where he leads a premiere software team building complex enterprise software products. Chad is a .NET software developer specializing in enterprise software designs and architectures. He has over 12 years of software development experience and a proven track record of Agile, test-driven project leadership using both Microsoft and open source tools. He is a community leader who speaks at the Austin .NET User's Group, the ADNUG Code Camp, and participates in various development communities and open source projects.
    This entry was posted in Extension Methods, Mocks. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
    • http://davesquared.blogspot.com David

      Hi Chad,

      There is already a method to do this, GetArgumentsForCallsMadeOn(), although the syntax is not quite as neat as your version.

      E.g.:

      [Fact]
      public void Can_check_argument_with_Rhino_Mocks() {
      var testMock = MockRepository.GenerateMock();
      testMock.DoSomething(10, “test”);
      string secondArgOfFirstCall = testMock
      .GetArgumentsForCallsMadeOn(x => x.DoSomething(0, null))
      .First()
      [1] as string;
      Assert.Equal(“test”, secondArgOfFirstCall);
      }

      public interface IAmJustForTesting {
      void DoSomething(int a, string b);
      }

      Cheers,
      David

    • http://davesquared.blogspot.com David

      Oops, still need to create the mock then. Please disregard last comment :)