Unit testing Expressions with Moq

When setting up a mock object with the Moq framework you can specify what parameters may be passed to the mock and thus what to return when the mock encounters those specific parameters.

This falls down in the odd instance when you’re trying to pass a lambda expression to an optional parameter. This occurs, for example, on IRepository Find() as the where: and the orderby: are both optional. You can’t pass an expression tree to an optional parameter as it’s not compiled yet.

Moq gets around this by allowing It.IsAny() so at least we can specify the type of the expression to accept. How do we know, though, whether our mocked interface was called correctly? Quite different expressions, and any parameters, could have been used.

Fortunately, you can access the actual expression used in the .Returns() callback on the mock. Here you can create an anonymous function that will test the signature, the parameters used and still return the object mothers you’ve specified.

Here’s what we’ve got so far.

   
       [TestMethod]
       public void ShouldFindUser()
       {
           Expression<Func> expected = 
               x => 
               x.Name == this.sut.Name && 
               x.Memberships[0].Password == 
                   this.sut.Memberships[0].Password;
           // note: optional parameters that are passed 
           // expression trees can't be compiled in .NET 4.0 
           // but Moq's It.IsAny saves the day.
           repository.Setup(
               r =>
               r.Find(
                   It.IsAny<Expression<Func>>(),
                   It.IsAny<IOrderByClause[]>()))
                .Returns(
                       (Expression<Func> where, 
                        IOrderByClause[] order) =>
                           {
                               // note: before the expressions can be 
                               // compared they must
                               // be partially evaluated.
                               this.ExpressionMatch(
                                   Evaluator.PartialEval(where), 
                                   Evaluator.PartialEval(expected));
                               return suts;
                           });

           service = new UserService(repository.Object);

           bool isValidUser = 
               this.service.ValidateUser(
                   this.sut.Name, 
                   this.sut.Memberships[0].Password);

           Assert.IsTrue(isValidUser, "Expected to find user.");
       }

Comparing the expressions, however, introduces more problems. The expressions have not been compiled yet so they have unevaluated references to closed variables. The expressions will differ between the actual expression and any expected expression you may have defined using your object mother for the parameters.

You need to partially evaluate the expressions to create constants from the references before you can compare them (the comparison is essentially comparing the two .ToString() products). Finally, you can wrap an unit test assertion around the equality comparison.

       private void ExpressionMatch(Expression actual, Expression expected)
       {
           var isEqual = 
               ExpressionEqualityComparer.ExpressionEqual(actual, expected);

           Assert.IsTrue(isEqual, "Expected the expressions to match.");
       }

Now if someone alters the code that calls to the interface the test will fail. Otherwise it would have been joyfully returning object mothers for any old query passed to the interface.

Advertisements

3 comments

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s