Является ли условная компиляция допустимой стратегией макета / заглушки для модульного тестирования?
-
01-07-2019 - |
Вопрос
В недавнем вопросе о заглушках во многих ответах предлагались интерфейсы C # или делегаты для реализации заглушек, но один ответ предложил использовать условную компиляцию, сохранив статическую привязку в производственном коде.Этот ответ был изменен на -2 на момент прочтения, так что по крайней мере 2 человека действительно подумали, что это неправильно отвечай.Возможно, причиной было неправильное использование DEBUG или, возможно, использование фиксированного значения вместо более тщательной проверки.Но я не могу перестать удивляться:
Является ли использование условной компиляции неподходящим методом для реализации заглушек модульных тестов?Иногда?Всегда?
Спасибо.
Редактировать-добавить: Я хотел бы добавить пример в качестве эксперимента:
class Foo {
public Foo() { .. }
private DateTime Now {
get {
#if UNITTEST_Foo
return Stub_DateTime.Now;
#else
return DateTime.Now;
#endif
}
}
// .. rest of Foo members
}
сравнивая с
interface IDateTimeStrategy {
DateTime Now { get; }
}
class ProductionDateTimeStrategy : IDateTimeStrategy {
public DateTime Now { get { return DateTime.Now; } }
}
class Foo {
public Foo() : Foo(new ProductionDateTimeStrategy()) {}
public Foo(IDateTimeStrategy s) { datetimeStrategy = s; .. }
private IDateTime_Strategy datetimeStrategy;
private DateTime Now { get { return datetimeStrategy.Now; } }
}
Который позволяет отключать исходящую зависимость от "DateTime.Now" через интерфейс C #.Однако теперь мы добавили вызов динамической отправки, где было бы достаточно статического, объект больше даже в рабочей версии, и мы добавили новый путь к сбою для конструктора Foo (выделение может завершиться ошибкой).
Неужели я здесь ни о чем не беспокоюсь?Спасибо за отзыв на данный момент!
Решение
Старайтесь хранить производственный код отдельно от тестового.Поддерживайте различную иерархию папок..различные решения/проекты.
Если не..вы находитесь в мире устаревшего кода на C ++.Здесь все идет своим чередом..если условные блоки помогут вам сделать часть кода тестируемой, и вы увидите выгоду..Во что бы то ни стало сделай это.Но постарайтесь, чтобы это не стало более беспорядочным, чем исходное состояние.Четко прокомментируйте и разграничьте условные блоки.Действуйте с осторожностью.Это допустимый метод для получения устаревшего кода в тестовом режиме.
Другие советы
Я думаю, что это снижает ясность для людей, просматривающих код.Вам не нужно помнить, что вокруг определенного кода есть условный тег, чтобы понять контекст.
Нет, это ужасно.Это приводит к утечке теста в ваш производственный код (даже если он отключен).
Плохо, очень плохо.
Тестовый код должен быть очевидным и не смешиваться в тех же блоках, что и тестируемый код.
Это почти та же причина, по которой вам не следует писать
if (globals.isTest)
Я подумал о другой причине, по которой это было ужасно:
Много раз, когда вы что-то имитируете / заглушаете, вы хотите, чтобы его методы возвращали разные результаты в зависимости от того, что вы тестируете.Это либо исключает это, либо делает это чертовски неловким.
Это может быть полезно в качестве инструмента, на который можно опереться при рефакторинге для обеспечения тестируемости в большой базе кода.Я вижу, как вы могли бы использовать такие методы, чтобы внести небольшие изменения и избежать рефакторинга "большого взрыва".Однако я бы беспокоился о том, чтобы слишком сильно не полагаться на такую технику, и постарался бы сделать так, чтобы такие трюки не жили слишком долго в базе кода, иначе вы рискуете сделать код приложения очень сложным и трудноуловимым.