Заглушите один метод класса и позвольте другим реальным методам использовать этот заглушенный

У меня есть класс TimeMachine, который предоставляет мне текущие значения даты / времени. Класс выглядит так:

public class TimeMachine
{
    public virtual DateTime GetCurrentDateTime(){ return DateTime.Now; };
    public virtual DateTime GetCurrentDate(){ return GetCurrentDateTime().Date; };
    public virtual TimeSpan GetCurrentTime(){ return GetCurrentDateTime().TimeOfDay; };
}

Я хотел бы использовать заглушку TimeMachine в своих тестах таким образом, чтобы я просто заглушил метод GetCurrentDateTime и позволил другим двум методам использовать заглушку GetCurrentDateTime, чтобы мне не пришлось заглушать все три метода. Я попытался написать такой тест:

var time = MockRepository.GenerateStub<TimeMachine>();
time.Stub(x => x.GetCurrentDateTime())
    .Return(new DateTime(2009, 11, 25, 12, 0, 0));
Assert.AreEqual(new DateTime(2009, 11, 25), time.GetCurrentDate());

Но тест не проходит. GetCurrentDate возвращает default(DateTime) вместо внутреннего использования GetCurrentDateTime заглушки.

Есть ли какой-либо подход, который я мог бы использовать для достижения такого поведения, или это просто какая-то базовая концептуальная особенность RhinoMocks, которую я не улавливаю в данный момент? Я знаю, что могу просто избавиться от этих двух _10 _ / _ 11_ методов и встроить использование _12 _ / _ 13_, но я хотел бы понять, возможно ли это вообще.


person Buthrakaur    schedule 25.11.2009    source источник


Ответы (5)


Если метод отмечен как virtual, заглушка не вызовет исходный метод, даже если вы не заглушили метод. Вы можете заставить RhinoMocks вызвать исходный метод, выполнив:

var time = MockRepository.GenerateStub<TimeMachine>();
time.Stub(x => x.GetCurrentDateTime()).Return(new DateTime(2009, 11, 25, 12, 0, 0));

time.Stub(x => x.GetCurrentDate()).CallOriginalMethod(OriginalCallOptions.NoExpectation);

Assert.AreEqual(new DateTime(2009, 11, 25), time.GetCurrentDate());

Это третья (разделенная) строка, которая заставляет RhinoMocks вызывать базовый исходный метод.

person Peter    schedule 05.03.2013
comment
+1 за единственный реальный ответ на вопрос (хотя и спустя 4 года) - person Riegardt Steyn; 08.09.2014

Измените свой TimeMachine на абстрактный класс:

public abstract class TimeMachine
{
    public abstract DateTime GetCurrentDateTime();
    public DateTime GetCurrentDate(){ return GetCurrentDateTime().Date; };
    public TimeSpan GetCurrentTime(){ return GetCurrentDateTime().TimeOfDay; };
}

Для производственных целей вы можете создать конкретную реализацию TimeMachine следующим образом:

public class SystemTimeMachine : TimeMachine
{
    public override DateTime GetCurrentDateTime()
    {
        return DateTime.Now;
    }
}

Все классы, потребляющие TimeMachine, теперь можно внедрить с помощью абстракции, но в производстве вы можете связать свой граф объектов с SystemTimeMachine.

person Mark Seemann    schedule 25.11.2009

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

public class TimeMachine
{
    public virtual DateTime GetCurrentDateTime(){ return DateTime.Now; };
    public DateTime GetCurrentDate(){ return GetCurrentDateTime().Date; };
    public TimeSpan GetCurrentTime(){ return GetCurrentDateTime().TimeOfDay; };
}

Тест пройден.

person Buthrakaur    schedule 25.11.2009
comment
Это может сделать тест пройденным, но методы следует делать виртуальными только в том случае, если класс предназначен для работы в качестве базового класса, а метод специально разработан для переопределения. Его не следует делать виртуальным только ради модульного тестирования. - person Adam Ralph; 25.11.2009
comment
Почему нет? Почему бы не сделать все методы виртуальными ради расширяемости? :) И даже при том, что я принимаю ваши аргументы, GateCurrentDateTime, как мне кажется, полностью подходит для переопределения. - person Buthrakaur; 26.11.2009
comment
Я согласен. Почему нет? Ну почти согласен. В Java каждый метод виртуален. Но тогда вам не было предоставлено никакой безопасности времени компиляции, что вы переопределяли правильный метод. Так было до тех пор, пока положение не спасло аннотации. Но .Net не нуждается в аннотации, потому что она заставляет вас объявлять метод как виртуальный, а метод переопределения - как переопределения. Но было бы неплохо, если бы для этого было лучшее решение. - person uriDium; 02.02.2010

Заглушка просто предоставляет стандартные ответы на вызовы методов и свойств, она ничего не знает о фактической реализации TimeMachine. Боюсь, вам придется настраивать результаты для каждого из 3 методов (или для конкретного метода, который вы хотите протестировать).

person Adam Ralph    schedule 25.11.2009
comment
Не думаю, что вы правы в случае заглушки реальных классов (не в случае интерфейсов). Заглушка просто является производным от класса и переопределяет его методы. Если метод не является виртуальным, его нельзя переопределить, и он остается функциональным, как реализовано реальным классом. - person Buthrakaur; 25.11.2009

Я не уверен, какую версию Rhino.Mocks вы используете, но я бы сделал виртуальным только метод GetCurrentDateTime () (как предлагалось ранее), а затем создал свой фиктивный объект с помощью PartialMock (). Есть много способов настроить вещи, но следующие должны работать:

var mocks = new MockRepository();
var time = mocks.PartialMock<TimeMachine>();
using (mocks.Record())
{
   Expect.Call(time.GetCurrentDateTime()).Return(new DateTime(2009, 11, 25, 12, 0, 0));
}
using (mocks.Playback())
{
   Assert.AreEqual(new DateTime(2009, 11, 25), time.GetCurrentDate());
}
person Eric Pohl    schedule 25.11.2009
comment
PartialMocks, и это использование записи / воспроизведения в RhinoMocks не рекомендуется. Но это решение, вероятно, эквивалентно моему предыдущему ответу. - person Buthrakaur; 26.11.2009