2013-06-21 5 views
2

Я знаю, что не могу использовать Moq, чтобы издеваться над вызовом статического метода внутри моего тестируемого метода, поэтому что мне нужно сделать, чтобы реорганизовать метод, чтобы я мог его протестировать? У меня также есть метод, вызывающий метод базового класса, мне нужно будет реорганизовать это, и если да, то как? Я не хочу использовать MS.Fakes или TypeMocks и создавать прокладки, я бы предпочел рефакторинг и написать твердый код!Как бы я реорганизовал статический метод, чтобы проверить свой метод?

public override DateTime ResolveDate(ISeries comparisonSeries, DateTime targetDate) 
    { 
     if (comparisonSeries == null) 
     { 
      throw new ArgumentNullException("comparisonSeries"); 
     } 

     switch (comparisonSeries.Key) 
     { 
      case SeriesKey.R1: 
      case SeriesKey.R2: 
      case SeriesKey.R3: 
      case SeriesKey.R4: 
      case SeriesKey.R5: 
       return DateHelper.PreviousOrCurrentQuarterEnd(targetDate); 
     } 

     return base.ResolveDate(comparisonSeries, targetDate); 
    } 

    [TestMethod] 
    public void SomeTestMethod() 
    { 
     var mockIAppCache = new Mock<IAppCache>(); 
     var mockISeries = new Mock<ISeries>(); 

     ReportFR2 report = new ReportFR2(SeriesKey.FR2, mockIAppCache); 
     DateTime resolvedDate = report.ResolveDate(mockISeries, DateTime.Now); 

     //Assert.AreEqual("something", "something"); 

    } 
+0

Или я хочу вызвать статический метод в моем тестируемом методе? Я думаю, что ответ отрицательный, потому что единичный тест проверяет только логику в тестируемом методе и ничего другого, кроме его частного метода. Кто-то поправьте меня, если я ошибаюсь! – user1186050

+2

Оберните DateHelper интерфейсом и введите свой IDateHelper в качестве параметра метода в содержащем классе или в качестве аргумента конструктора так же, как вы делаете withIAppCache? –

+0

Что было бы лучше? 1) оберните DateHelper интерфейсом и передайте его или 2) изолируйте статический метод в методе «защищенный внутренний виртуальный» в классе, а затем я мог бы издеваться над этим методом. – user1186050

ответ

2

Глядя выше, у вас есть три основных условия, которые вы можете проверить:

  1. При Сравнение серии нуль

  2. Когда ключ серии сравнения R1: R5

  3. Если ключ серии сравнения не является нулевым и ничего, кроме R1: R5

В состоянии 1 вы можете легко покрыть это с помощью теста.

В состоянии 2, когда это R1: R5, то есть там, где он появляется, он попадает в ваш статический метод.

  • С точки зрения метода ResolveDate вам все равно, какое значение имеет значение, когда оно попадает в эту ветку.
  • Было бы неплохо доказать, что R1: R5 вызывает статический помощник, но, как упоминалось в комментариях, единственный способ сделать это - обернуть DateHelper интерфейсом и передать его конструктору этот класс. Это может не стоить усилий.
  • Если вы решили не делать этого, я предлагаю вам провести тест, который попадает в эту ветку, а затем также написать другой набор тестов, предназначенных непосредственно для вашей функции DateHelper.PreviousOrCurrentQuarterEnd(), попав во все крайние случаи. Это поможет изолировать, какой код является виновником, если что-то не удается.

То же самое можно сказать и для условия 3 как условие 2. Хотя он и находится в вашем базовом классе, он по-прежнему является действительной ветвью логики.

  • Опять же, вы будете иметь трудное время доказывающий, что называется ваш базовый класс,

  • Но это все еще действует, чтобы проверить результат.

Так что, я думаю, что у вас есть четыре набора тестов вы можете писать, чтобы начать, а затем после того, как вы их прохождение, вы можете решить, если вы хотите взять на рефакторинга свой служебный класс DateHelper. Я думаю, вы «не буду говорить не приходится :-D

  1. Учитывая класс ReportRF2 и нуль-ряд Сравнение

    • При вызове report.ResolveDate

    • Должно быть выбрано исключение Null. Использовать
      `Assert.Throws (() => report.ResolveDate (null, DateTime.Теперь));

  2. Учитывая класс ReportRF2 и ключ серии в наборе R1: R5

    • Когда Разрешающая даты для граничного X (например, 1/1/0001), она должна быть равна у;

    • Когда «..» для ..., ...; (Повторить для края/граничных случаях, рассмотреть возможность использования Driven Data)

  3. Учитывая класс ReportRF2 и ключ серии NOT в наборе R1: R5 и NOT NULL

    • при разрешении Дата для границы X, ... аналогична # 2, но, вероятно, разные ожидаемые результаты.
  4. Учитывая статические утилиты класса DateHelper

    • при расчете PreviousOrCurrentQuarterEnd() и дата Х, она должна быть равна у,
    • похожи на крайние случаи в # 2 выше.

Это будет затем дать вам охват ожидаемых результатов, и говорит вам неудачи, вытекающие из метода ResolveDate или ваш метод DateHelper.PreviousOrCurrentQuarterEnd(). Он может не изолировать столько, сколько хотел бы пурист, но пока вы закрываете свои крайние случаи и ваши счастливые пути, это доказывает, что ваше приложение функционирует так, как планировалось (пока те тесты проходят).

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

1

Просто, чтобы добавить к @ хороший ответ Деймона, обернув DateHelper с интерфейсом можно сделать довольно легко:

public interface IDateHelper 
{ 
    DateTime PreviousOrCurrentQuarterEnd(DateTime targetDate); 
} 

Как отмечалось выше, вам потребуется класс экземпляра, который реализует этот интерфейс, но только для ваш код продукции, так как юнит-тестов будет просто использовать Mock<IDateHelper:

public class InstanceDateHelper : IDateHelper 
{ 
    public DateTime PreviousOrCurrentQuarterEnd(DateTime targetDate) 
    { 
     return DateTimeHelper.PreviousOrCurrentQuarterEnd(targetDate); 
    } 
} 

Voilà, теперь вы можете издеваться интерфейс IDateHelper и у вас есть реализация, использующая существующий статический код.

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

Picture этот метод:

public bool StartProcessAndWaitForExit(ProcessStartInfo info) 
{ 
    var process = Process.Start(info); // test-hindering static method call 
    //... 
} 

Единственное изменение, которое я должен был сделать было это:

public bool StartProcessAndWaitForExit(IProcessWrapper process, ProcessStartInfo info) 
{ 
    var process = process.Start(info); // injected wrapper interface makes method testable 
    //... 
} 

Если ResolveDate это единственный метод в классе, который требует IDateHelper, впрыскивая его в качестве параметр метода прекрасен; если у вас есть куча методов, которые все бы нуждаются в этом, то инъекция его в качестве аргумента конструктора и создание поля private readonly IDateHelper _helper; (инициализированное в конструкторе) - лучший способ.

 Смежные вопросы

  • Нет связанных вопросов^_^