Как имитировать вызов метода из другого класса в Rhino Mock AAA?

У меня есть следующий код (упрощенный).

public class OrderProcessor
{
    public virtual string PlaceOrder(string test)
    {
        OrderParser orderParser = new OrderParser();
        string tester =  orderParser.ParseOrder(test);
        return tester + " here" ;
    }

}

public class OrderParser
{

    public virtual string ParseOrder(string test)
    {
        if (!string.IsNullOrEmpty(test.Trim()))
        {
            if (test == "Test1")
                return "Test1";
            else
            {
                return "Hello";
            }
        }
        else
            return null;
    }
}

Мой тест выглядит следующим образом -

   public class OrderTest
   {
      public void TestParser()
      {
        // Arrange

        var client = MockRepository.GenerateMock<OrderProcessor>();
        var spec = MockRepository.GenerateStub<OrderParser>();

        spec.Stub(x => x.ParseOrder("test")).IgnoreArguments().Return("Test1");

        //How to pass spec to client so that it uses the same.

        }
    }

Теперь, как мне протестировать клиент, чтобы он использовал фиктивный метод из OrderParser. Я могу издеваться над OrderParser, но как мне передать это классу, сымитированному orderProcessor?

Пожалуйста, дайте мне знать.

Заранее спасибо.


person Prashant    schedule 23.09.2009    source источник


Ответы (1)


Я немного смущен вашим тестом, поскольку вы на самом деле ничего не тестируете, кроме того, что RhinoMocks работает. Вы создаете два макета, а затем делаете над ними какие-то утверждения. Вы даже не проверили свои настоящие классы.

Вам нужно сделать некоторую инъекцию зависимостей, если вы действительно хотите получить хороший модульный тест. Вы можете быстро реорганизовать свой код, чтобы использовать интерфейсы и внедрение зависимостей, чтобы сделать ваш тест действительным.

Начните с извлечения интерфейса из вашего класса OrderParser:

public interface IOrderParser
{
    String ParseOrder(String value);
}

Теперь убедитесь, что ваш класс OrderParser реализует этот интерфейс:

public class OrderParser: IOrderParser{ ... }

Теперь вы можете реорганизовать свой класс OrderProcessor, чтобы получить экземпляр объекта IOrderParser через его конструктор. Таким образом вы "внедряете" зависимость в класс.

public class OrderProcessor
{
    IOrderParser _orderParser;

    public OrderProcessor(IOrderParser orderParser)
    {
        _orderParser = orderParser;
    }

    public virtual string PlaceOrder(string val)
    {
        string tester =  _orderParser.ParseOrder(val);
        return tester + " here" ;
    }
}

В своем тесте вы хотите имитировать только зависимость, а не SUT (испытуемый предмет). Ваш тест будет выглядеть примерно так:

   public class OrderTest
   {
      public void TestParser()
      {
        // Arrange

        var spec = MockRepository.GenerateMock<IOrderParser>();
        var client = new OrderProcessor(spec);

        spec.Stub(x => x.ParseOrder("test")).IgnoreArguments().Return("Test1");

        //Act
        var s = client.PlaceOrder("Blah");

        //Assert
        Assert.AreEqual("Test1 Here", s);

        }
    }

Мне трудно оценить, что вы пытаетесь сделать со своими классами, но вы должны получить представление об этом. Несколько аксиом для подражания:

  1. Используйте интерфейсы и композицию вместо наследования
  2. Используйте внедрение зависимостей для внешних зависимостей (инверсия управления)
  3. Протестируйте один модуль и смоделируйте его зависимости
  4. Имитация только одного уровня зависимостей. Если вы тестируете класс X, который зависит от Y, который зависит от Z, вы должны издеваться только над Y и никогда над Z.
  5. Всегда тестируйте поведение и никогда не детализируйте реализацию

Кажется, вы на правильном пути, но вам нужно небольшое руководство. Я бы посоветовал прочитать материалы, написанные Мартином Фаулером и Боб Мартин должен набрать скорость.

person Josh    schedule 23.09.2009