Пересечение объектов со сложными лямбда-выражениями в качестве параметров - PullRequest
0 голосов
/ 26 апреля 2010

Я сталкиваюсь с этой проблемой, пытаясь смоделировать некоторые объекты, которые получают сложные лямбда-выражения в моих проектах. В основном с прокси-объектами, которые получают этот тип делегата:

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 просто он никогда не оправдывает ожиданий.

Ответы [ 2 ]

1 голос
/ 27 апреля 2010

Я нашел способ обойти это с помощью 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

Существует исправление для пересмотра параметров лямбда-выражений, однако оно работает только с простыми лямбда-выражениями. Вы можете получить это здесь

0 голосов
/ 28 апреля 2010

Наконец я передумал. Вернуться к основам.

Мне нужно знать, вызывается ли метод 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.

...