使用复杂的Lambda表达式作为参数模拟对象

时间:2010-04-26 13:52:56

标签: c# unit-testing mocking rhino-mocks moq

我在尝试模拟一些在我的项目中接收复杂lambda表达式的对象时遇到了这个问题。主要是使用接收此类委托的代理对象:

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

我曾尝试使用Moq和RhinoMocks来模拟这些类型的对象,但两者都失败了。

这是我尝试做的简化示例:首先,我有一个计算器对象进行计算:

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;
     }
}

接下来,我需要验证Calculator类中每个方法的参数,因此为了遵守Single Responsibility原则,我创建了一个验证器类。我使用Proxy类连接所有内容,以防止重复代码:

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; }
 }

最后,我正在测试一个使用CalculatorProxy的组件,所以我想模仿它,例如使用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();
 }

然而我无法让它发挥作用! Moq因NotSupportedException而失败,而且在RhinoMocks中它简直无法满足期望。

2 个答案:

答案 0 :(得分:1)

我已经找到了解决方法,使用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();
    }

这样我可以测试通过计算器对象上的计算器代理调用的方法,验证MathParser是否解析了表达式。

我想我可以把它转化为我的真实项目。

另外,我发现在Moq中,Lambda Expression参数支持是一个未解决的问题,针对最终的4.0版本:Moq Open Issues

修复了使用lambda表达式参数进行模拟但是它只适用于简单的lambda表达式。你可以得到它here

答案 1 :(得分:0)

最后我改变了主意。回归本源。

我需要知道的是,是否使用正确的参数调用Calculator.Add方法。所以考虑到单元测试覆盖了代理,我认为我应该模拟Calculator对象,并使用真正的代理。在不改变测试意义的情况下,它比我之前的解决方案更清晰。

使用Moq看起来像这样:

    [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();
    }

我也开始喜欢Moq语法而不是Rhino.Mocks。