Что такое юнит-тестирование черного ящика?

11

Недавно у меня был последний экзамен по курсу разработки программного обеспечения для моей магистерской программы, и один из вопросов на экзамене был следующим:

Unit Testing is considered:
a. White-box Testing
b. Black-box Testing
c. Either

В моем 7-летнем опыте разработки программного обеспечения модульное тестирование всегда проводилось по принципу «белого ящика». При написании тестов тестировщик всегда имел полное представление о реализации модуля. Тестирование черного ящика всегда приходило позже в виде интеграционных, системных и приемочных испытаний.

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

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

Может кто-нибудь объяснить мне, как работает модульное тестирование черного ящика (если оно действительно есть) и чем оно отличается от модульного тестирования белого ящика?

backcab
источник
4
Как профессор объяснил это, когда вы спросили их об этом? (см. также Почему вопросы интервью делают плохие вопросы Software Engineering.SE? )
gnat
«кто пишет тест обычно имеет довольно хорошее представление о том , как тест будет реализован» , - это не о том, знаете ли вы , как реализован тест, но знаете ли вы (белую) или нет (черная) , насколько вещь Вы тестируете это реализовано.
Jesper
@ Джеспер, извини. Я хотел сказать «как будет реализован исходный код». Я исправил это в вопросе.
Backcab
2
While the implementation does not yet exist, whoever is writing the test generally has a pretty good idea about how the source code is going to be implemented.- Да, но сам тест не дает. Тестирование белого ящика означает тестирование чего-то внутреннего для метода или класса, например, значения переменной. Это не означает, что автор теста знает, как выглядит тестируемый код.
Роберт Харви
1
Связанный, может рассматриваться как дубликат: использует ли TDD формально тестирование черного ящика для дополнения модульных тестов
Док Браун

Ответы:

20

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

При тестировании черного ящика вы смотрите только на интерфейс и (если он существует) спецификацию для компонента. Когда функция имеет сигнатуру int foo(int a, int b), я могу сразу создать несколько тестовых случаев, просто протестировав интересные целые числа: ноль, один, минус один, многозначные числа, INT_MAX, INT_MAX - 1 и так далее. Тесты черного ящика хороши тем, что они независимы от реализации. Но они также могут пропустить важные дела.

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

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

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

Если вы проводите разработку через тестирование, то теоретически все ваши юнит-тесты должны быть «черным ящиком». Это ваш "первый подход к тестированию". Вы пишете контракт (интерфейс), пишете тесты для этого контракта, и затем контракт выполняется реализацией. Поэтому тест ничего не знает и ничего не должен знать о реализации.

В конце концов, когда вы пишете тест, что вы тестируете? Публичные методы / функции.

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

В некоторой степени это рушится, когда вам нужно высмеивать что-то, от чего зависит реализация, но если вы окажетесь в ситуации, когда вы издеваетесь над чем-то, что никогда не раскрывается публично, то вы допустили ошибку и вам нужно посмотрите на Dependency Injection et al . Поэтому я бы сказал, что модульное тестирование белого ящика, а не черного, должно быть исключением.

Рассмотрим «Тестирование в туалете - поведение теста, а не реализацию» , в котором реализация класса изменяется, но тесты все еще должны быть действительными.

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

AdamJS
источник
2
If you were to write the interface for a class, and then write the tests, and then you get hit by a bus, the guy who writes the class while you're in hospital should be able to do so from your interface, right?-- Не совсем. Большинство контрактов API действительно определяют только сигнатуры методов, а не семантику или поведение.
Роберт Харви
Вы правы; Я считал само собой разумеющимся, что ваш интерфейс будет включать спецификацию, из которой он был написан, а не просто текст MyClassInterface.
AdamJS
@RobertHarvey это правда, что большинство интерфейсов явно не описывают семантику или поведение, но я думаю, что это вообще там неявно. Если этого не было, то код, который требует определенной семантики, не сможет зависеть от абстракции. И нет ничего, что могло бы остановить интерфейсы, включая детали семантики и поведения в качестве комментариев / docblocs. Для примера см github.com/php-fig/http-message/blob/master/src/...
bdsl
3

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

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

Вывод: если вы тестируете белый ящик с юнит-тестами, у вас плохо сконструированы тесты. Только бэкбокс тест с этими юнит-тестами. Вы, профессор, правы: это может быть и так. Но только если сделано плохо.

Дэвид Арно
источник
1

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

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

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

Shooresh
источник