Когда мне следует писать интеграционные тесты?

30

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

Должны ли они быть написаны до модульных тестов или после производственного кода только для проверки «проводки»?

Обратите внимание, что я говорю не о приемочных или функциональных тестах, а об интеграционных тестах более низкого уровня.

Chedy2149
источник

Ответы:

49

Книга Rspec , среди других ресурсов BDD, предлагает такой цикл:

введите описание изображения здесь

По сути, этот процесс:

While behaviour required
    Write an integration test for a specific behaviour
    While integration test failing
        Write a unit test to fulfil partial behavior
        While unit test failing
            Write code to make unit test pass
        Commit
        While refactoring can be done
            Refactor
            While unit test failing
                Write code to make unit test pass
            Commit
    Push

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

Тем не менее, иметь в виду идеальный процесс это здорово. Это дает вам точку для компромисса.

прецизионный самописец
источник
2
Спасибо @pdr, но я указал, что я не говорю о приемочных тестах, которые написаны до / в начале итерации, меня интересуют интеграционные тесты более низкого уровня.
Chedy2149
@ chedy2149: Акх. Пропустил этот комментарий. Прежде чем я удалю свой ответ, я думаю, что вы должны быть более конкретными о том, что вы подразумеваете под «низкоуровневым» в контексте интеграционных тестов.
фунтовые
Нижний уровень: какое поведение не указано пользователями или клиентами и какое используется для проверки взаимодействия классов / компонентов, ожидаемое разработчиками.
Chedy2149
5
На самом деле, я не думаю, что имеет значение, если вы добавите «приемочный тест» или «интеграционный тест» на эту картинку - это идеализированное представление для любого вида тестов на разных уровнях абстракции. Но ИМХО, реальная проблема не в том, что это может быть «отнимает много времени» - реальная проблема в том, что написание интеграционных тестов «заранее» на общедоступном интерфейсе, который все еще разрабатывается с помощью TDD, похоже на стрельбу по «движущейся цели». ».
Док Браун
@DocBrown, так что ваш ответ после производственного кода и перед выпуском?
Chedy2149
10

Реальный проект показал мне, что нельзя писать модульные тесты, и тогда интеграция и даже противоположное направление неправильны :-) Поэтому я обычно пишу модульные тесты вместе с интеграционными.

Зачем? Позвольте мне написать, как я вижу оба вида тестов:

  1. Модульные тесты - В дополнение к Википедии и всем известной информации, модульные тесты помогают вам сузить ваш дизайн , улучшить вашу модель, отношения. Процесс прост: как только вы начинаете вводить новый проект / новый компонент, большую часть времени вы делаете какой-то PoC . Когда вы закончите, у вас всегда будут длинные методы, длинные классы, некогерентные методы и классы и т. Д.

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

  2. Интеграционные тесты - правильные и рабочие тесты говорят вам, что ваш новый компонент (или компоненты) работают вместе или с другими компонентами - это обычное определение. Я обнаружил, что интеграционные тесты в основном помогают определить, как использовать ваш компонент со стороны потребителя .

    Это действительно важно, так как иногда вам говорят, что ваш API не имеет смысла извне.

Ну, что произойдет, когда я напишу модульные тесты и интеграционные тесты позже?

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

Ну, что произойдет, когда я напишу интеграционные и модульные тесты позже?

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

Какой мой совет?

Я изучил следующий поток:

  1. Разработайте базовый каркас вашего кода
  2. Напишите интеграционные тесты, которые говорят, имеет ли это смысл с точки зрения потребителя. Базового варианта использования пока достаточно. Тест явно не работает.
  3. Напишите код вместе с модульными тестами для каждого класса.
  4. Напишите остаток / отсутствие интеграционных тестов. Было бы лучше реализовать эти тесты в # 3, как вы улучшаете свой код.

Обратите внимание, что я сделал небольшую презентацию о модульном / интеграционном тестировании, см. Слайд № 21, где описан скелет.

Мартин Подвал
источник
5

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

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

Бен Макдугалл
источник
Итак, ваш ответ после кода производства?
Chedy2149
Это не отвечает на вопрос. Он спрашивает, написан ли производственный код после написания интеграционных тестов. Ваш ответ может быть принят в любом случае.
Reactgular
1
@MathewFoscarini - обновленный ответ. Надеюсь, теперь это станет понятнее.
Бен Макдугалл
Что касается модульных тестов, то я бы не согласился с «наименьшим возможным битом программного обеспечения». Проверьте, что в контракте (например, открытые методы объекта, экспортированные функции библиотеки), потому что контракт определяет, что должно работать. Другие вещи поддаются проверке, но это не только пустая трата времени, но и контрпродуктивно.
Брюс
3

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

Я предпочитаю писать их перед производственным кодом. Это имеет то преимущество, что помогает мне вспомнить, какие части я еще не подключил, или что я немного изменил детали взаимодействия объектов.

Schleis
источник
Существуют разные уровни тестирования: тестирование компонентов белого ящика, тестирование компонентов интеграции белого ящика. Тестирование компонентов черного ящика, тестирование интеграции черного ящика. Также есть тестирование системы интеграции.
Александр.
2

Помимо приемочных тестов, я обычно пишу интеграционные тесты только на границах приложения, чтобы убедиться, что оно хорошо интегрируется со сторонними системами или компонентами.

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

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

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

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

Вы можете найти примеры такого подхода здесь: http://davesquared.net/2011/04/dont-mock-types-you-dont-own.html (6-й абзац) http://blog.8thlight.com/eric- кузнец / 2011/10 / 27 / Thats-не-yours.html

guillaume31
источник
здесь вы говорите об интеграционных тестах, которые проверяют взаимодействие между «нашей системой» и сторонними библиотеками, как насчет тестирования взаимодействий с платформой при разработке плагина, например?
Chedy2149
Хотя у меня мало опыта разработки плагинов, я думаю, что они могут отличаться, поскольку по своей природе они тесно связаны с хост-приложением, поэтому вы можете полностью принять эту интеграцию и решить, что вам не нужен уровень адаптера. В этом случае я буду очень осторожен с производительностью тестов - в зависимости от хост-приложения вызов его API напрямую в большом количестве ваших тестов может быть очень медленным. Если вы боитесь этого, вы всегда можете прибегнуть к подходу «дополнительный уровень абстракции» и использовать mocks + интеграционные тесты на адаптерах.
guillaume31
1

Итак, я собирался принять первый ответ, но он был удален.
Подводя итог
в данной итерации:

  1. Написать юнит-тест
  2. Написать производственный код
  3. Написать интеграционные тесты для тестирования взаимодействия

Имейте в виду интеграционное тестирование при 1 и 2, чтобы гарантировать тестируемость на уровне интеграции.

Интеграционные тесты не обязательно записываются из конца в конец на шаге 3, они могут быть частично записаны между шагами 1 и 2.

Chedy2149
источник
3
Это суммирование полностью игнорирует итеративную природу процесса. Когда у вас будет стабильный API вашего производственного кода, вы можете начать писать интеграционные тесты. Затем вы сможете снова работать над рабочим кодом и, возможно, изменить или расширить свои интеграционные тесты. Таким образом, в большинстве случаев вы не «пишете интеграционные тесты после производственного кода», вы обычно выполняете оба в некоторой степени параллельно. И на самом деле, это также сильно зависит от того, какое программное обеспечение вы работаете. «Черно-белое» мышление не ведет вас дальше.
Док Браун
Хороший вопрос. Ответ, похоже, игнорирует итеративную природу дизайна посредством рефакторинга.
Chedy2149
0

Модульные тесты тестируют отдельные блоки кода в вашем проекте.
Интеграционные тесты проверяют, как ваш код взаимодействует с другим кодом: другими словами, они тестируют интерфейс вашего кода.

Напишите модульные тесты при разработке кода за интерфейсом.
Напишите интеграционные тесты при разработке интерфейса или любого кода, который реализует интерфейс.

Это означает, что вы иногда будете писать интеграционные тесты очень поздно в проекте, потому что большая часть работы находится за интерфейсом: например, компилятор, конкретный веб-сервис, который реализует несколько уровней логики или ... что-то, что включает в себя много внутренняя логика

Однако, если вы внедряете набор служб REST или реорганизуете модель данных и добавляете поддержку транзакций XA, вы начнете разрабатывать интеграционные тесты практически сразу, потому что большая часть вашей работы сосредоточена вокруг интерфейса, будь то REST API или как программа использует модель данных.

Marco
источник
Согласны ли вы сказать, что модульные тесты - это тестирование в «белых ящиках», а интеграционные тесты - это тесты в «черных ящиках»?
Chedy2149
К сожалению, это зависит. В последние годы технологии для интеграционного тестирования значительно улучшились (по крайней мере, в мире Java), поэтому я могу тестировать 1 класс - но на сервере приложений. Тогда возникает вопрос: где грань между юнит-тестами и интеграционными тестами? Является ли интеграционный тест, когда вы тестируете код, взаимодействующий с другими технологиями, или интеграционным тестом, когда вы тестируете свое приложение целиком - но не обязательно в среде, в которой оно предназначено для запуска?
Марко
Короче говоря, в некоторых случаях определенные интеграционные тесты - это тестирование черного ящика, но не во всех случаях.
Марко
К вашему сведению: Wiki определяет интеграционное тестирование как «фазу тестирования программного обеспечения, в которой отдельные программные модули объединяются и тестируются как группа»
Марко
Точно, Марко. На каждом уровне компонентов есть интеграции. Уровень кода, уровень приложения, уровень системы.
Александр.