views:

219

answers:

2

I´m encountering this problem trying to mock some objects that receive complex lambda expressions in my projects. Mostly with with proxy objects that receive this type of delegate:

Func<Tobj, Fun<TParam1, TParam2, TResult>>

I have tried to use Moq as well as RhinoMocks to acomplish mocking those types of objects, however both fail.

This is simplified example of what I´m trying to do: first, I have a Calculator object that does calculations:

public class Calculator
{
     public int Add(int x, int y)
     {
          var result = x + y;
          return result;
     }  

     public int Substract(int x, int y)
     {
           var result = x - y;
           return result;
     }
}

Next, I need to validate parameters on every method in the Calculator class, so to keep with the Single Responsibility principle, I create a validator class. I wire everything up using a Proxy class, that prevents having duplicate code:

public class CalculatorProxy : CalculatorExample.ICalculatorProxy
{
    private ILimitsValidator _validator;

    public CalculatorProxy(Calculator _calc, ILimitsValidator _validator)
    {
        this.Calculator = _calc;
        this._validator = _validator;
    }

    public int Operation(Func<Calculator, Func<int, int, int>> operation, 
                         int x, 
                         int y)
    {
        _validator.ValidateArgs(x, y);

        var calcMethod = operation(this.Calculator);

        var result = calcMethod(x, y);

        _validator.ValidateResult(result);

        return result;
     }

     public Calculator Calculator { get; private set; }
 }

Finally, I´m testing a component that does use the CalculatorProxy, so I want to mock it, for example using Rhino Mocks:

[TestMethod]
public void ParserWorksWithCalcultaroProxy()
{

    var calculatorProxyMock = MockRepository.GenerateMock<ICalculatorProxy>();

    calculatorProxyMock.Expect(x => x.Calculator).Return(_calculator);

    calculatorProxyMock.Expect(x => x.Operation(c => c.Add, 2, 2)).Return(4);

    var mathParser = new MathParser(calculatorProxyMock);

    mathParser.ProcessExpression("2 + 2");

    calculatorProxyMock.VerifyAllExpectations();
 }

However I cannot get it to work! Moq fails with NotSupportedException, and in RhinoMocks simpy it never gets to satisfy the expectations.

+1  A: 

I have found a way around this using Moq:

    [TestMethod]
    public void ParserWorksWithCalcultaroProxy()
    {
        var calculatorProxyMock = new Mock<ICalculatorProxy>();
        Func<Calculator, Func<int, int, int>> addMock = c => c.Add;

        calculatorProxyMock.Setup(x => x.BinaryOperation(It.Is<Func<Calculator, Func<int, int, int>>>(m => m(_calculator) == addMock(_calculator)), 2, 2))
                                  .Returns(4).Verifiable();           

        var mathParser = new MathParser(calculatorProxyMock.Object);

        mathParser.ProcessExpression("2 + 2");

        calculatorProxyMock.Verify();
    }

This way i can test what method is being called through the calculator proxy on the calculator object, verifying that the MathParser does it job parsing the expression.

I think Im going to be able to traslate this to my real projects.

Also, I found that in Moq, Lambda Expression parameter support is an open issue, that is targeted to the final 4.0 release: Moq Open Issues

There is a fix to mocking with lambda expression parameters however it does work only with simple lambda expressions. You can get it here

iCe
Also I´m starting to think that is better in this case to mock only the calculator object, and use the real proxy. I have the proxy covered with tests so there should be no problem. Anyway this is more a functional test than a unit test, so I think is ok going that way.
iCe
A: 

Finally I have changed my mind. Back to basics.

What I need to know is whether the Calculator.Add method gets called with the correct arguments. So given that it have the proxy covered by unit tests, I think that I should mock the Calculator object, and use the real proxy. It's way clearer than my previous solution without changing the meaning of the test.

Using Moq looks like this:

    [TestMethod]
    public void ParserWorksWithCalcultaroProxy()
    {
        var calculatorMock = new Mock<CalculatorExample.ICalculator>();

         calculatorMock.Setup(x => x.Add(2, 2)).Returns(4).Verifiable();

        var validatorMock = new Mock<ILimitsValidator>();

        var calculatorProxy = new CalculatorProxy(calculatorMock.Object, validatorMock.Object);

        var mathParser = new MathParser(calculatorProxy, new MathLexer(new MathValidator()));
        mathParser.ProcessExpression("2 + 2");

        calculatorMock.Verify();
    }

Also I´m starting to prefer Moq syntax instead of Rhino.Mocks.

iCe