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

发布于 2024-08-31 00:36:23 字数 1962 浏览 2 评论 0原文

我在尝试模拟一些在我的项目中接收复杂 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 中它永远无法满足期望。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

铁轨上的流浪者 2024-09-07 00:36:24

我找到了一种使用 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 开放问题

有一个针对使用 lambda 表达式参数进行模拟的修复,但它仅适用于简单的 lambda 表达式。您可以在此处获取它

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

送你一个梦 2024-09-07 00:36:24

最后我改变了主意。回到基础。

我需要知道的是 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。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文