代码之家  ›  专栏  ›  技术社区  ›  Iñaki Elcoro

用复杂lambda表达式模拟对象作为参数

  •  0
  • Iñaki Elcoro  · 技术社区  · 14 年前

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

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

    我试过用moq和犀牛模型来模拟这些类型的物体,但都失败了。

    这是我要做的事情的简化示例:首先,我有一个计算器对象来进行计算:

    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类中每个方法的参数,因此为了遵循单一责任原则,我创建了一个验证程序类。我使用代理类连接所有内容,这样可以防止重复代码:

    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模拟:

    [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失败了,没有支持例外,而且在Rhinomoks Simpy中,它永远无法满足预期。

    2 回复  |  直到 14 年前
        1
  •  1
  •   Iñaki Elcoro    14 年前

    我找到了一种方法来解决这个问题,使用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表达式参数支持是一个开放性问题,它针对的是最终的4.0版本: Moq Open Issues

    对lambda表达式参数模拟有一个修复,但是它只对简单lambda表达式有效。你可以得到它 here

        2
  •  0
  •   Iñaki Elcoro    14 年前

    我终于改变主意了。回到基础。

    我需要知道的是是否使用正确的参数调用calculator.add方法。因此,考虑到它具有由单元测试覆盖的代理,我认为我应该模拟计算器对象,并使用真正的代理。在不改变测试含义的情况下,它比我以前的解决方案更清晰。

    使用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。