Mocking objects with complex Lambda Expressions as parameters
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 valida开发者_C百科tor 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.
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
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.
精彩评论