В чем разница между интеграцией и юнит-тестами? - PullRequest
281 голосов
/ 14 августа 2008

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

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

Действительно ли эти тесты стали интеграционными, потому что теперь они тестируют интеграцию этих 2 классов, или это просто модульный тест, охватывающий 2 класса?

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

Я неправильно понимаю интеграционные тесты, или на самом деле разница между интеграцией и модульными тестами очень мала?


Редактировать

Спасибо всем за отличные ответы! Я думаю, что из разных ответов ясно, что грань между юнит-тестами и интеграционными тестами определенно размыта, и, возможно, немного педантично пытаться выяснить, какие из них и на чем стоит сосредоточиться (спасибо @Rob Cooper ). Кроме того, извините, но я не собираюсь принимать какой-либо ответ, потому что слишком многие слишком хороши, и это действительно кажется весьма субъективным.

Ответы [ 19 ]

274 голосов
/ 24 октября 2011

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

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

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

Скажем, у вас есть метод где-то вроде этого:

public SomeResults DoSomething(someInput) {
  var someResult = [Do your job with someInput];
  Log.TrackTheFactYouDidYourJob();
  return someResults;
}

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

Feature: To be able to do something
  In order to do something
  As someone
  I want the system to do this thing

Scenario: A sample one
  Given this situation
  When I do something
  Then what I get is what I was expecting for

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

Если вы хотите написать модульный тест для DoSomething, вы должны сделать вид (используя некоторые макеты), что остальные классы и методы работают (то есть: все зависимости, которые использует метод, работают правильно) и утверждать, что ваш метод работает.

На практике вы делаете что-то вроде:

public SomeResults DoSomething(someInput) {
  var someResult = [Do your job with someInput];
  FakeAlwaysWorkingLog.TrackTheFactYouDidYourJob(); // Using a mock Log
  return someResults;
}

Вы можете сделать это с помощью Dependency Injection, с помощью некоторого Factory Method или любой Mock Framework или просто путем расширения тестируемого класса.

Предположим, есть ошибка в Log.DoSomething(). К счастью, спецификация Gherkin найдет его, и ваши сквозные тесты не пройдут.

Функция не будет работать, потому что Log не работает, а не потому, что [Do your job with someInput] не выполняет свою работу. И, кстати, [Do your job with someInput] является единственной ответственностью за этот метод.

Также предположим, что Log используется в 100 других функциях, в 100 других методах 100 других классов.

Да, 100 функций не удастся. Но, к счастью, 100 сквозных тестов также дают сбой и выявляют проблему. И да: они говорят правду .

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

Тем не менее, DoSomething юнит-тест зеленый, потому что он использует фальшивый Log, созданный, чтобы никогда не ломаться. И да: это явно вранье . Это сообщение сломанной функции работает. Чем это может быть полезно?

(Если модульный тест DoSomething() не пройден, убедитесь, что в [Do your job with someInput] есть некоторые ошибки.)

Предположим, что это система со сломанным классом: A system with a broken class

Одна ошибка нарушит несколько функций, а несколько интеграционных тестов не пройдут.

A single bug will break several features, and several integration tests will fail

С другой стороны, та же самая ошибка будет нарушать только один модульный тест.

The same bug will break just one unit test

Теперь сравните два сценария.

Эта же ошибка сломает только один юнит-тест.

  • Все ваши функции с использованием ломаной Log - красные
  • Все ваши юнит-тесты зеленые, только юнит-тест для Log красный

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

Разница

Интеграционные тесты сообщают , что не работает. Но они бесполезны в предположении, где проблема может быть.

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

Вот почему я думаю, что ваше предложение «Или это просто юнит-тест, охватывающий 2 класса» каким-то образом смещено. Модульный тест никогда не должен охватывать 2 класса.

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

61 голосов
/ 01 сентября 2008

Когда я пишу модульные тесты, я ограничиваю область тестируемого кода классом, который я сейчас пишу, путем имитации зависимостей. Если я пишу класс Sentence, а Sentence зависит от Word, я буду использовать фиктивный Word. Подшучивая над Word, я могу сосредоточиться только на его интерфейсе и протестировать различные варианты поведения моего класса Sentence при взаимодействии с интерфейсом Word. Таким образом, я только тестирую поведение и реализацию предложения, а не одновременно тестирую реализацию Word.

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

43 голосов
/ 14 августа 2008

Мои 10 битов: D

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

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

Хотя это настоящая подвижная строка ... Я бы лучше сосредоточился на том, чтобы заставить этот чертов код работать на полную остановку ^ _ ^

22 голосов
/ 03 августа 2009

В модульных тестах используются макеты

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

17 голосов
/ 14 августа 2008

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

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

Технически, вы все равно не можете тестировать только один класс. Что если ваш класс состоит из нескольких других классов? Это автоматически делает это интеграционным тестом? Я так не думаю.

12 голосов
/ 14 августа 2008

с использованием единого дизайна ответственности, его черный и белый. Более 1 ответственности, это интеграционный тест.

По тесту на утку (внешний вид, кряк, вейдл, это утка), это всего лишь модульный тест с более чем 1 новым объектом.

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

10 голосов
/ 22 сентября 2014

характер ваших тестов

A модульный тест модуля X - это тест, который ожидает (и проверяет) проблемы только в модуле X.

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

Подумайте о природе ваших тестов в следующих терминах:

  • Снижение риска : Для этого и нужны тесты. Только комбинация модульных тестов и интеграционных тестов может дать вам полное снижение риска, потому что, с одной стороны, модульные тесты по своей сути не могут проверить правильное взаимодействие между модулями, а с другой стороны, интеграционные тесты могут использовать функциональность нетривиальный модуль только в небольшой степени.
  • Тестирование написания текста : Интеграционные тесты могут сэкономить усилия, потому что вам может не понадобиться писать заглушки / подделки / макеты. Но модульные тесты также могут сэкономить усилия, если реализовать (и поддерживать!) Эти заглушки / подделки / mocks оказывается проще, чем конфигурировать настройку теста без них.
  • Задержка выполнения теста : Интеграционные тесты, включающие тяжелые операции (такие как доступ к внешним системам, таким как БД или удаленные серверы), имеют тенденцию быть медленными (ошибочно). Это означает, что модульные тесты могут выполняться гораздо чаще, что сокращает усилия по отладке в случае неудачи, потому что вы лучше представляете, что вы изменили за это время. Это становится особенно важным, если вы используете разработку через тестирование (TDD).
  • Отладка : Если интеграционный тест не пройден, но ни один из модульных тестов не выполнен, это может быть очень неудобно, поскольку в коде так много всего, что может содержать проблему , Это не большая проблема, если вы ранее изменили только несколько строк - но поскольку интеграционные тесты выполняются медленно, вы, возможно, не выполняли их с такими короткими интервалами ...

Помните, что интеграционный тест по-прежнему может заглушить / подделать / макетировать некоторые его зависимостей. Это обеспечивает достаточное промежуточное положение между юнит-тестами и системными тестами (наиболее комплексные интеграционные тесты, тестирование всей системы).

Прагматичный подход к использованию обоих

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

8 голосов
/ 21 июля 2013

На мой взгляд, ответ «Почему это важно?»

Это потому, что модульные тесты - это то, что вы делаете, а интеграционные тесты - это то, что вы не делаете? Или наоборот? Конечно, нет, вы должны попытаться сделать оба.

Это потому, что юнит-тесты должны быть быстрыми, изолированными, повторяемыми, самоподтверждаемыми и своевременными, а интеграционные - не должны? Конечно, нет, все тесты должны быть такими.

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

Это потому, что модульные тесты тестируют один модуль, а интеграционные тесты - несколько? Конечно, нет. Какое практическое значение это имеет? Теоретическое обсуждение объема тестов в любом случае нарушается на практике, поскольку термин «единица измерения» полностью зависит от контекста. На уровне класса юнит может быть методом. На уровне сборки единица может быть классом, а на уровне обслуживания единица может быть компонентом. И даже классы используют другие классы, так что же это за единица?

Это не важно.

Тестирование важно, F.I.R.S.T важно, раскалывание определений - пустая трата времени, которое только вводит в заблуждение новичков в тестировании.

4 голосов
/ 13 июля 2012

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

4 голосов
/ 14 августа 2008

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...