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

У меня есть класс 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 stub в своих тестах таким образом, чтобы я просто заглушил метод GetCurrentDateTime, а другие 2 метода использовали обрезанный метод 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, которую я сейчас не поймаю? Я знаю, что могу просто избавиться от этих двух методов GetDate/Time и включить использование .Date/.TimeOfDay, но я хотел бы понять, возможно ли это вообще.

Ответ 1

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

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

Теперь проходит тест.

Ответ 2

Измените 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.

Ответ 3

Если метод помечен как virtual, Stub не будет вызывать оригинальный метод, даже если вы не запустили метод. Вы можете заставить 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 вызывать базовый, оригинальный метод.

Ответ 4

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

Ответ 5

Я не уверен, какую версию 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());
}