Generics and NMock

Maybe NMock supports this, but I can’t see how.

[Update 20/10/2007 – of course it does! See the comments for Aybars and Rob’s solution.]

Let’s say I have a dependency of my SUT (system under test). That dependency looks like:

public interface IDatabase
{
  void Get<TTypeToFetch> ();
}

Now in my test, I want to expect that the SUT will try to fetch a Widget.

NMock gives me something which looks like it could help – a matcher. So I can code something like this:

Mockery mockery = new Mockery();
IDatabase db = mockery.NewMock<IDatabase>();
Expect
  .Once
  .On(db)
  .Method(new GenericMethodMatcher(Widget));

So I code up a little GenericMethodMatcher which casts the object passed to it as a MethodInfo and takes a look at the result of a call to GetGenericArguments() to see if it fins a Widget. So far, so exceedingly proper bo.

Unfortunately, the MethodInfo which NMock passes to the matcher is ‘open’, meaning that it still contains just the templated definitions of the types that could be passed to it at run-time. What I need in this instance, is a ‘closed’ MethodInfo object.

I guess this is hard for NMock to do, since it only works with the interface and not a concrete class. I have no idea what’s going on under the hood… Maybe it it possible but I’m just too stupid… I wonder how other mocking frameworks handle this scenario.

Published by Matt

I write software, and love learning how to do it even better.

Join the Conversation

1 Comment

  1. Here’s how its done:

    
    Expect
       .Once
       .On(StubbedInvoker.fooFacade)
       .Method(new GenericMethodMatcher("GetFooFor", typeof(ExpectedBar)))
       .With(prop1).Will(Return.Value(List<ExpectedBar>)); 
    
    GetFooFor<UnExpectedBar>(prop1);
    
    

    Here’s the matcher. Basically it uses reflection (MethodInfo). It caught us out at first as the first time it hits the matcher (when you set the expectation) as the method had not been made concrete (ContainsGenericParameters). It hits the Matcher again when the method gets called and voila the test fails.

    public class GenericMethodMatcher : Matcher
    {
        private readonly string methodName;
        private readonly Type expectedType;
        private string errortext = String.Empty;
    
        public GenericMethodMatcher(string methodName, Type expectedType)
        {
             this.methodName = methodName;
              this.expectedType = expectedType;
        }
    
        public override bool Matches(object o)
        {
            MethodInfo methodInfo = o as MethodInfo;
    
            if (methodInfo.Name == methodName)
            {
               //if this is true it means the method has not been called yet so will not have our argument yet.
               if (methodInfo.ContainsGenericParameters)
                   return true;
    
               foreach (Type type in methodInfo.GetGenericArguments())
               {
                   if (expectedType == type)
                       return true;
                   }
                   errortext = string.Format("Generic method{0} called with unexpected type parameter. Expected: {1} ", methodName, expectedType);
                       return false;
                    }
                    return false;
                }
    
                public override void DescribeTo(TextWriter writer)
                {
                    Console.WriteLine(errortext);
                }
            }
        }
    }
    

Leave a comment

Your email address will not be published. Required fields are marked *