Tuesday, November 22, 2011

Moq Testing and Lambda Equivalence

I have been steadily improving my unit tests as my experience with Moq improves, but I have now encountered an issue that has thrown me a little.

To recap the app design, I am using EF Code First for the ORM, accessed via Repository classes that expose (mockable) operations on the repository. I then have a service layer that performs the business operations across multiple repositories.

My unit testing is focusing on the Service Layer methods, with (partially) mocked repositories. My initial tests were pretty simple setup the mocks, perform the action, assert the response. However as I became more familiar with the Moq Verify function I was able to improve the way I tested by actually verifying that my repositories were being accessed with the expected parameters.
An example is that when my service method GetItem(itemId) is called, I expect my repository method

to be called with a value of itemId.
I can then test using the following

The test above ensures that calling GetItem on my service calls the GetSingle on my repository with the expected parameter, and calls it only once. It is a very basic test for a simple method, but is a good example.

The issue is that my repository is a bit more complex than I have shown, where the Get methods actually accept Lambda expressions, so we use

instead of just

Now this actually works IF we are using static/const variables in the collection, for example

Item class has a static const ITEM_TYPE_BOX = "Box"
and my service class calls

If we are passing variables to the service method, and that variable is used to create the repository expression, the test fails as the Verify method cannot find a matching execution of the method.
My service method accepts a string itemType

and my unit test uses the following verify method

The test code above fails because while the two lamdba expressions are functionallity identical, they are not expressively equal, so the Moq verify function comparison fails. Drilling down into the issues I have found that the expression specifically includes the namespace of the method that creates the expression when using local types, which means creating the lambda expression in one class, and comparing it to an identical expression created in another class will always fail (you can see this if you create an expression and call ToString() on the expression). The reason it works for constant comparisons is that there are no local variables to compare to.

I cannot remove the dependency on lambdas in my repository as this forms the core of the repository flexibility, but I have identified one way of keeping my unit tests robust while overcoming this issue.
It is possible to expose methods in the service that create an expression object which can be used by both the unit test verify, and the service method. The issue with this is that it is somewhat cumbersome since you need to expose a method for each of the expression combinations your service is using. In many cases this will be relatively straight forward, but it is still a fair bit of effort.

This is pretty disappointing as the unit testing was going quite well up to this point.

No comments:

Post a Comment