View on GitHub

wiki

Technical Excellence Wiki

Unit-тест

Перевод статьи на русский язык Мартина Фаулера UnitTest

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

Sketch Картинка взята из оригинальной статьи

Хотя я провел огромное количество unit-тестирований и до этого, моя окончательная позиция сформировалась когда я начал работать с Кентом Бэком и начал использовать семейство тестовых инструментов Xunit. На самом деле, я иногда думаю более удачное название для этого стиля тестирования может быть “xunit-тестирование”. Unit-тестирование также стало знаковой активностью Экстремального Программирования (XP) и быстро привело к TDD.

Были определенные сомнения насчёт применения unit-тестирования в XP c самых ранних дней. Я четко помню обсуждения в usenet-группах в которых XPшники ругали эксперта по тестированию за неправильное использование понятия “модульный тест”. Мы спросили его про определение, и он ответил что-то вроде “в первом дне моего тренинга я рассказываю про 24 разных определения модульного теста.”

Несмотря на различия, есть некоторые общие элементы. В первую очередь, в том, что unit-тесты - низкоуровневые, фокусируются на маленькой части программной системы. Во-вторых, unit-тесты в наши дни пишутся самими разработчиками, используя свои обычные инструменты — единственное отличие в использовании определенного вида фреймворка для unit-тестирования1. В-третьих, unit-тесты должны быть значительно быстрее чем остальные виды тестов.

Итак, если есть общие элементы, то значит есть и различия. Одно отличие в том, что люди называют модулем(“unit”). Объектно-ориентированный дизайн стремится к использованию класса в качестве модуля, в процедурных и функциональных подходах можно считать функцию в качестве модуля. Но на самом деле это зависит от ситуации — команда сама решает что имеет смысл считать модулем в целях их понимания системы и её тестирования. Хотя я начал с того, что модуль это класс я часто делаю набор тесно связанных классов и рассматриваю их как единый модуль. Редко я могу взять набор методов класса в качестве модуля. Как вы его определите на самом деле не так важно.

Изолированный или коммуникабельный?

Более важное различие в том должен ли быть модуль который вы тестируете изолированным (solitary) или коммуникабельным (sociable) 2. Представьте что вы тестируете метод price() класса Order. Метод price() должен запустить некоторые функции в классах Product и Customer. Если вы любите чтобы ваши тесты были изолированными, то вы не хотите использовать настоящие классы Product или Customer, потому, что ошибка в классе Customer приведет к падению теста на класс Order. Вместо этого вы используете тестовые дублёры в качестве “соучастников”.

Unit Testing Картинка взята из оригинальной статьи

Однако, не все unit-тестировщики использую изолированные unit-тесты. На самом деле когда xunit-тестирование началось в 90х, мы не писали изолированные тесты до тех пор, пока взаимодействие не становилось затруднительным (таким как например, внешняя система для проверки кредитных карт). Мы не считали, что сложно отследить реальную причину ошибки, даже если это станет причиной падения соседних тестов. Поэтому, мы думали что если мы дадим нашим тестам возможность взаимодействовать с несколькими классами, это не приведёт к проблемам.

На самом деле, коммуникабельные тесты были одной из причин по которой нас критиковали за использование понятия “unit-тестирования”. Я считаю что понятие “unit-тестирование” подходящим потому, что эти тесты проверяют поведение одного модуля. Мы писали тесты предполагая, что все остальное работает корректно.

По мере того как xunit-тестирование становилось более популярным, в 2000х вернулось упоминание изолированных тестов, по крайней мере для некоторых людей. Мы видели восхождение Mock-объектов и фреймворков, поддерживающих подмену объектов(mocking). Возникли две школы xunit-фреймворков, которые я называю классическая и мок-школа. Одно из отличий между двумя стилями в том что мокисты настаивают на изолированных unit-тестах, а классики предпочитают коммуникабельные тесты. Сегодня я знаю, и уважаю xunit-тестировщиков следующих обоим стилям (я следую классическому стилю).

Даже классические тестировщики, такие как я, используют тестовые дублёры когда есть неудобное взаимодействие в коде. Они бесценны, для того чтобы избавиться от недетерминированности при работе с удаленными сервисами. На самом деле, некоторые классические xunit-тестировщики также спорят должно ли каждое взаимодействие со внешними ресурсами, такими как база данных или файловая система, использовать тестовые дублеры. Частично это из-за риска недетерминированности, частично из-за скорости. Я считаю это хорошей рекомендацией, и не считаю что использование дублеров для всех внешних ресурсов является обязательным правилом. Если вы взаимодействуете со стабильным ресурсом и достаточно быстро, то у вас нет причин не поступать также и в ваших unit-тестах.

Скорость

Обычное свойство unit-тестов - их небольшой охват, который создает разработчик собственноручно и высокая скорость — это означает то, что они могут часто запускаться во время программирования. На самом деле, это одна из ключевых характеристик Самотестирующегося кода. В этой ситуации разработчики запускают unit-тесты несколько раз в минуту, каждый раз у нас есть код, который стоит того, чтобы его компилировать. Я это делаю, потому что я могу сломать что-то случайно, и я хочу сразу знать об этом. Если я внес дефект во время моей последней правки так мне намного проще найти баг, потому, что не нужно заглядывать слишком далеко назад.

Когда вы запускаете unit-тесты так часто, то вы можете не запускать все unit-тесты. Обычно вам нужно запустить только те тесты, которые проверяют ту частью кода, над которой вы сейчас работаете. Как обычно, это компромисс между глубиной тестирования и тем, как долго занимает прогон тестов. Я называю такие тесты набором для компилирования (compile suite), тк это то, что я запускаю когда, хочу скомпилировать, даже в интерпретируемых языках, таких как Ruby.

Если вы используете Непрерывную Интеграцию вы должны запускать тесты те это является частью этой практики. Обычно при коммите прогоняют набор тестов, который я называю набор для коммита (commit suite), в который входят все unit-тесты. Также, он может включать несколько более верхнеуровневых тестов. Как разработчику, вам следует запускать этот набор для коммита несколько раз в день, естественно перед тем как размещать ваш код в систему контроля версий, но у вас также есть возможность в любое другое время запускать тесты — во время вашего перерыва или если вам необходимо пойти на встречу. Чем быстрее набор тестов, тем более часто вы сможете его запускать3.

У разных людей есть разные стандарты по скорости unit-тестов и их тестовых наборов. Дэвид Хайнемейер Хэнссон доволен набором тестов для сборки, который пробегает несколько секунд и набором тестов для коммита, который проходит за несколько минут. Гарри Бернхардт считает, что это невыносимо долго, настаивая на том, что при компиляции тесты должны пробежать примерно за 300 мс, а Дэн Бодарт не хочет чтобы его коммит проходил более чем 10 секунд.

Я не думаю что есть единый абсолютно правильный ответ. Я лично не замечаю разницы между прогоном тестов меньше секунды или если это занимает несколько секунд. Мне нравится золотое правило Кент Бэка, что тесты при коммите не должны бегать более чему 10 минут. Идея в том, чтобы ваши тесты должны выполняться достаточно быстро, так чтобы у вас не пропадало желание запускать их часто. И достаточно часто это так часто, чтобы когда вы обнаружите баг то вам надо было бы просмотреть достаточно маленький объём работы для того, чтобы вы смогли быстро найти ошибку.

Заметки

  1. Я говорю “наши дни” потому, что определенно что-то изменилось из-за ХР. В начале столетия XPшников жестко критиковали, так как существовало распространенное мнение, что разработчики не должны тестировать свой собственный код. Некоторые организации специализировались на unit-тестировании, их единственная задача была в написании unit-тестов, на код, написанный разработчиками. Причины этого были: у людей концептуальная слепота при тестировании собственного кода, разработчики - плохие тестировщики, и в том, что хорошо когда между разработчиками и тестировщиками есть здоровая конкуренция. Взгляд XPшников был в том, что разработчики могут научиться быть эффективными тестировщиками, по крайней мере на уровне модульного тестирования и в том что когда вы вовлекаете отдельную группу цикл обратной связи, которую дают вам unit-тесты безнадежно длинный. Xunit сыграл здесь важную роль, он был спроектирован специально для того, чтобы минимизировать проблемы для разработчиков при написании тестов.

  2. Джей Филдс пришел к терминам “solitary” and “sociable”

  3. Если у вас есть полезные тесты, но на их запуск уходит больше времени чем бы вам хотелось, то вам следует создать deplyment-пайплайн и поместить более медленные тесты в более позднюю стадию этого “трубопровода”

Перевел: Сергей Лобин

Остались вопросы? Задавай в нашем чате.