A Type Safe ExpectCall Extension Method for Reflective TypeMocks

An entry about tdd | c# 3.0 Publication date 28. January 2008 23:48

Lately, I've been upping the ante on my unit testing skills, which has led me to the awesome mocking framework called TypeMock. Basically, it uses the Profiling API to perform some AOP magic which allows just about anything to be mocked - without it having been explicitly designed for testability, which the merits of is a heavily debated issue.

Imagine that we have the following code that we want to test:

public class Foo
{
    public void DoSomething()
    {
        Logger.Instance.Log("Logging something");
    }
}
 
public class Logger
{
    private Logger() { }
 
    public static readonly Logger Instance = new Logger();
 
    public void Log(string message)
    {
        // logs message to a table in the database
    }
}

 

Normally, writing a unit test for the Foo.DoSomething method would be painful, because we would have to deal with the fact that the Log method it calls needs a database to do its thing, even though we don't really care about the Log method (it should have its own test to verify its correctness, after all).

If we were designing for testability, we would solve this by implementing the logging as a pluggable API so that we could inject a dummy implementation of it that didn't depend on any external resources when testing the Foo class. However, oftentimes the ability to inject a mock implementation of some API turns out to only be a requirement to make the code testable, leading us to sacrificing the cohesion of our code in favor of loose coupling.

With TypeMock, however, we can write our test like this:

[TestMethod]
public void TestDoSomething()
{
    MockManager.Init();
    Mock loggerMock = MockManager.Mock<Logger>();
 
    loggerMock.ExpectCall("Log").Args("Logging something");
 
    Foo foo = new Foo();
    foo.DoSomething();
 
    MockManager.Verify();
}

 

What we're doing here, is telling TypeMock that we want to mock the Logger class, and that the next call to the method Log should be intercepted and faked, instead of actually calling the real method. Additionally, we tell it that we expect the method call to have the argument 'Logging something'. At the end of the test, we ask TypeMock to verify our expectations, and fail the test if any were not met.

It would be much better, though, if I could tell TypeMock this in a type-safe manner, because unit tests are, naturally, written very early on in the coding - there are bound to be countless refactorings of class and method names afterwards. With the above test, however, if I rename the Log method then the test will break.

Now, TypeMock does have a solution for this called Natural TypeMocks, but sadly its not available in the free edition of the framework. So, until I can convince the people in charge of the money at my firm to shell out for licenses to all our devs, I've come up with a neat little extension method inspired by the ASP.NET MVC Toolkits clever use of expression trees to do runtime analysis of lambdas. With it, I can rewrite my above test to:

[TestMethod]
public void TestDoSomething2()
{            
    MockManager.Init();
    Mock loggerMock = MockManager.Mock<Logger>();
 
    loggerMock.ExpectCallAndVerifyArguments<Logger>(l => l.Log("Logging something"));
 
    Foo foo = new Foo();
    foo.DoSomething();
 
    MockManager.Verify();
}

 

Now that is pretty damn cool if you ask me :) Obviously, it has a few limitations - most notably it will only for public members on non-static classes. Regardless, I think it might prove pretty useful - at least until I can get my hands on a license for the Professional or Enterprise edition :p

Here's the code for the extension method:

public static class MockExtensions
{
    public static void ExpectCallAndVerifyArguments<T>(this Mock mock, Expression<Action<T>> expression)
    {
        // get the call expression
        MethodCallExpression call = expression.Body as MethodCallExpression;
 
        // which allows us to pick out the method name
        string methodName = call.Method.Name;                    
 
        // which we can use to set the method call expectation
        IParameters callResult = mock.ExpectCall(call.Method.Name);
 
        // next, we figure out what parameters the expression contains, if any
        // following code is based on the ASP.NET MVC Toolkit LinkExtension.cs implementation
        ParameterInfo[] parameters = call.Method.GetParameters();
 
        if (parameters.Length > 0)
        {
            List<object> args = new List<object>();
 
            for (int i = 0; i < parameters.Length; i++)
            {
                ParameterInfo parameter = parameters[i];
                Expression arg = call.Arguments[i];
                object value;
                ConstantExpression ce = arg as ConstantExpression;
 
                if (ce != null)
                {
                    // If argument is a constant expression, just get the value
                    value = ce.Value;
                }
                else
                {
                    // Otherwise, convert the argument subexpression to type object,
                    // make a lambda out of it, compile it, and invoke it to get the value
                    var lambda = Expression.Lambda<Func<object>>(Expression.Convert(arg, typeof(object)));
 
                    try
                    {
                        value = lambda.Compile()();
                    }
                    catch
                    {
                        value = null;
                    }
                }
 
                args.Add(value);
            }
 
            // set the list of expected parameters
            callResult.Args(args.ToArray());
        }
    }
}

 

In case you couldn't read it between the lines of this post - I'm really exited about TypeMock. So far I've only barely scratched the surface of it, and already its helped me broaden my understanding of unit testing, and allowed me to do really cool things like the above - strong indications of a truly great product, I'd say. So far I've only experimented with implementing an extension method for call expectations, but similar extensions should be easily applicable to other parts of the mocking functionality of TypeMock - I might do a follow-up post with more on this topic in the not too distant future :)

Be the first to rate this post

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Comments

Powered by BlogEngine.NET 1.4.5.0

Welcome!

My name is Fredrik Kalseth, and this is my blog - thanks for visiting! I am fortunate enough to work with what I love for a living, and this blog is essentially the biproduct of that.

I work as a senior consultant for Capgemini, and am also an active participant in the Norwegian .NET community, as an avid attendee but also as a speaker (most recently at NNUG and MSDN Live).

As a developer, I have a wide circle of interest. My primary passion is for agile, test-driven development, with focus on best practices and clean code. That said, I also love to work on the frontend, especially with web development.

On Twitter? My handle is fkalseth. On LinkedIn? I`m there too.

Disclaimer

This is a personal blog; any opinions expressed here are my own and do not necessarily reflect those of my employer. All content herein is my own original creation, and as such is protected by copyright law. Unless otherwise stated, all source code posted on this blog is freely usable under the Microsoft Permissive License.

What Readers Talk About

Comment RSS