【问题标题】:Mocking objects with complex Lambda Expressions as parameters使用复杂的 Lambda 表达式作为参数模拟对象
【发布时间】:2010-04-26 13:52:55
【问题描述】:

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

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

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

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

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 类中每个方法的参数,因此为了保持单一职责原则,我创建了一个验证器类。我使用 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 simpy 中它永远无法满足期望。

【问题讨论】:

  • @iCe - 编号列表与您的代码区域发生冲突,这使得问题难以理解,因此我删除了它们。如果您不喜欢这些更改,我深表歉意,请随时回滚!
  • 感谢 Jeff,我试图在没有运气的情况下删除代码区域的奇怪编码!
  • 我认为您在创建 MathParser 之前缺少“calculatorProxyMock.ReplayAll()”。在您运行测试时,您的模拟对象仍处于“记录”模式(但我认为如果您仍处于记录模式,VerifyAllExpectations 会失败?哦,这也是我更喜欢 AAA sytnax 的另一个原因)。
  • 这适用于旧版本的 Rhino.Mocks,但在当前版本中它是隐式完成的。我使用 Rhino.Mocks 的方式是 AAA 语法:ayende.com/blog/archive/2008/05/16/…

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


【解决方案1】:

我找到了一种使用最小起订量的方法:

    [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】:

我终于改变了主意。回归本源。

我需要知道的是是否使用正确的参数调用了 Calculator.Add 方法。因此,鉴于它具有单元测试涵盖的代理,我认为我应该模拟 Calculator 对象,并使用真正的代理。在不改变测试含义的情况下,它比我之前的解决方案更清晰。

使用起订量如下所示:

    [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。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-24
    • 1970-01-01
    相关资源
    最近更新 更多