Модульное тестирование виртуальных методов в Linq - PullRequest
2 голосов
/ 16 сентября 2010

У меня есть следующая структура класса, которая мне нужна для модульного тестирования:

public interface IFoo
{
    int Value { get;}
    int GetValue();
}

public class BaseClass : IFoo
{
    public virtual int Value { get { return 100; } }
    public virtual int GetValue()
    {
        return Value;
    }
}

public class ChildClass : BaseClass, IFoo
{
    public override int GetValue()
    {
        return Value;
    }
}

Я пытаюсь протестировать метод ChildClass.GetValue () с RhinoMocks.

У меня есть следующий код заглушки:

public ChildClass CreateChildClass(int value)
{
   var childClass = MockRepository.GenerateStub<ChildClass>();
   childClass.Stub(x => x.Value).Return(value);
   return childClass;            
}

И следующий код UnitTest:

public IEnumerable<IFoo> CreateList()
{
   yield return CreateChildClass(1000);
   yield return CreateChildClass(2000);
}

[TestMethod]
public void virtualMethodTest()
{
   var list = CreateList();
   var query = from p in list
               select p.GetValue(); //I can't use p.Value

   var sum = query.Sum(p => p);
 }

Проблема в том, что сумма всегда равна нулю.Я могу быстро просмотреть список и увидеть два фиктивных объекта с правильными значениями из CreateList ().

Можно ли выполнить модульное тестирование виртуального метода в дочернем классе?или мне нужно использовать другой подход RhinoMocks?

Спасибо

1 Ответ

2 голосов
/ 16 сентября 2010

Две вещи:

  • Вы создаете заглушки конкретного класса, а не IFoo. Моки имеют высокую отражающую способность в своей внутренней работе, и поэтому полиморфные вызовы могут вести себя или не вести себя одинаково в зависимости от структуры и точного характера вызова.
  • Вы создаете заглушки, которые по своей природе будут игнорировать любые вызовы (возвращающие значение по умолчанию для возвращаемого типа) в их интерфейс, которые не ожидаются. Это прямая причина вашей проблемы; GetValue () не ожидался, поэтому он возвращает целочисленное значение по умолчанию (0).

Вы хотите протестировать некоторое поведение реального класса, но переопределить определенное другое поведение (побочные эффекты, не входящие в область теста и т. Д.). Основной ответ в RhinoMocks - это PartialMock, который по умолчанию будет соответствовать фактическому поведению класса, кроме случаев, когда вы ожидаете, что будет сделан определенный вызов. Поэтому вместо GenerateStub <> () создайте экземпляр MockRepository и создайте новый MockRepository.PartialMock (). Ожидайте внутреннее поведение, которое вы хотите изменить, но больше ничего не трогайте. Возможно, вам придется использовать более старый метод записи и воспроизведения; версия RhinoMocks, с которой я обычно работаю, работает неправильно, когда вы создаете экземпляр PartialMock и затем вызываете Expect () для экземпляра.

Изменение будет выглядеть следующим образом:

//instantiate Mocks in class scope, as an instance of MockRepository

public ChildClass CreateChildClass(int value)
{
   var childClass = Mocks.PartialMock<ChildClass>();
   childClass.Expect(x => x.Value).Return(value);
   return childClass;            
}
...