2010-11-18 5 views
6

Я читал, что статические методы избегают при использовании TDD, потому что они, как правило, трудно издеваться. Тем не менее, я считаю, что самая простая вещь для модульного теста - это статический метод, который имеет простую функциональность. Не нужно создавать экземпляры каких-либо классов, поощряет методы, которые простые, делают одно, «автономны» и т. Д.Статические методы и модульные тесты

Может ли кто-нибудь объяснить это несоответствие между лучшими практиками TDD и прагматичной легкостью?

спасибо,

ответ

14

Статический метод легко проверить, но то, что непосредственно вызывает статический метод, как правило, не легко проверить зависит от статического метода это зависит. С помощью нестатического метода вы можете использовать экземпляр stub/mock/fake для упрощения тестирования, но если тестируемый вами код вызывает статические методы, он эффективно «жестко связан» с этим статическим методом.

+0

Я думаю, что термин вы ищете для плотно соединены. –

+1

@Martin: Спасибо, я знаю этот термин, хотя он более общий, чем то, что я получаю здесь. Обычно используемые термины часто теряют смысл, потому что люди (ab) используют их так много, поэтому я предпочел использовать метафору здесь, надеясь, что это станет более ясным. –

+0

Первый раз слышится «жесткий провод» вместо плотно соединенного. Совершенно блестяще, как изменение условий может помочь в понимании. – jrahhali

2

Легко проверить статический метод. Проблема в том, что нет способа изолировать ваш другой код от этого статического метода при тестировании другого кода. Вызывающий код тесно связан с статическим кодом.

Ссылка на статический метод не может издеваться над многими смехотворными фреймворками и не может быть переопределена.

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

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

3

Ответ на заданный вопрос, по-моему, «Объектно-ориентированный, похоже, все, о чем думают люди TDD».

Почему? Я не знаю. Возможно, это все программисты на Java, которые были заражены болезнью за счет того, что все полагается на шесть слоев косвенности, инъекции зависимостей и интерфейсные адаптеры.

Java-программисты, похоже, любят все усложнять, чтобы «сэкономить время позже».

Советуем применить к вашему TDD некоторые гибкие принципы: если это не вызывает проблемы, тогда не исправляйте это. Не над дизайном.

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

Если статические методы выполняются быстро, то им не нужен макет.

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

Если вам нужно издеваться над статическим методом, помните, что есть способы сделать это снаружи программирования OO.

Например, вы можете писать сценарии для обработки исходного кода в тестовую форму, которая вызывает функцию макета.Вы можете связать различные объектные файлы, которые имеют разные версии функции в тестовых программах. Вы можете использовать уловки-линкера, чтобы переопределить определение функции (если оно не получилось вложенным). Я уверен, что есть еще несколько трюков, которые я не перечислял здесь.

+0

Я согласен с вами в простых случаях. Многие статические методы - это простые вызовы утилиты, которые являются статическими, поскольку они не имеют состояния. Но другие статические вызовы ссылаются на глобальные (статические) переменные или другие статические вызовы, и это может быть очень сложно управлять в единичной тестовой среде. Вы должны убедиться, что глобальное состояние является правильным для каждого тестового прогона - иначе вы получите flakiness (тесты, которые работают изолированно, но не работают, когда остальная часть пакета работает). Так что это могут быть червяки, но я согласен, что вы должны прагматично относиться к этому. – sheikhjabootie

0

Этот совет верен по большей части .. но не всегда. Мои комментарии не C++ конкретные ..

  1. написания тесты для статических методов (которые являются чистыми/лицами без функции): то есть работа от входов для получения согласованного результата. например Добавить ниже - всегда будет давать одинаковое значение с учетом определенного набора входов. Существует без проблем в письменных тестах для этих или кодов, которые называет такие чистые статические методы.
  2. письменные тесты для статических методов, которые потребляют статическое состояние: например. GetAddCount() ниже. Вызов его в нескольких тестах может дать разные значения. Следовательно, один тест может потенциально повредить выполнение другого теста - тесты должны быть независимыми. Итак, теперь нам нужно ввести метод сброса статического состояния, чтобы каждый тест мог начинаться с чистого листа (например, что-то вроде ResetCount()).
  3. Письменные тесты для кода, который обращается к статическим методам , но Отсутствие доступа к зависимостям исходного кода: снова зависит от свойств самих статических методов. Однако, если они грубые, у вас сложная зависимость. Если зависимость является объектом, то вы можете добавить установщик к зависимому типу и установить/ввести поддельный объект для своих тестов. Когда зависимость статична, вам может понадобиться значительный рефакторинг, прежде чем вы сможете надежно выполнить тесты. (Например Добавить среднего человека зависимость объекта, который делегирует статический метод. Теперь плагин поддельный среднего человека-для испытаний)

Давайте рассмотрим пример

public class MyStaticClass 
{ 
    static int __count = 0; 
    public static int GetAddCount() 
    { return ++__count; } 

    public static int Add(int operand1, int operand2) 
    { return operand1 + operand2; } 

    // needed for testability 
    internal static void ResetCount() 
    { 
    __count = 0; 
    } 
} 

... 

//test1 
MyStaticClass.Add(2,3);  // => 5 
MyStaticClass.GetAddCount(); // => 1 

// test2 
MyStaticClass.Add(2,3); // => 5 
//MyStaticClass.ResetCount(); // needed for tests 
MyStaticClass.GetAddCount(); // => unless Reset is done, it can differ from 1 
Смежные вопросы