Про комбинаторику много сказано, еще больше будет сказано, и мне бы тоже хотелось что-нибудь сказать, но т.к. я очень люблю рассматривать обе стороны медали, статья
Bj Rollison'а Combinatorial Testing: Testing with Negative Values привлекла мое внимание. Автор использует инструмент PICT (заранее поясняю, т.к. об инструменте он ведет разговор с самого начала, а его название дает только в конце).
Перевод мой. ))
Большинство жителей западного Вашингтона впервые увидели зиму в этот понедельник. В этом году зима обещала быть суровой, она началась перед Днем Благодарения со снегом и очень низкой температурой. Вчера утром я проснулся от слепящего солнца, отблескивающего от прекрасного белого ковра глубиной 25 сантиметров. Это был прекрасный день, я взял лыжи и пошел гулять по окрестностям. Домой вернулся примерно через 2 часа и отправился кататься на санках со своей дочерью. Конечно, прежде, чем выпить горячего шоколада у огня, нам пришлось сделать несколько снежных ангелов.
До сих пор в нашей дискуссии о комбинаторном тестировании наша модель и результирующие тесты были сфокусированы на допустимых входящих значениях [имеются в виду предыдущие статьи]. Это широко известно как позитивное тестирование, потому что все допустимые вводимые комбинации дают допустимые исходящие значения или состояния. Тестирование различных комбинаций допустимых значений для нескольких входных параметров, которые влияют на общее состояние исходящих значений или условий, иногда демонстрирует неожиданные ограничения, что также помогает повысить общую достоверность тестирования фичи. Позитивное тестирование должно основываться на клиенте (большинство клиентов не используют отрицательные значения, чтобы увидеть сколько ошибок они могут найти) при оценке поведения или исходящих значений, а зачастую проще разработать автоматизированные оракулы для позитивных тестов, чем разрабатывать их для негативных тестов. В основном, автоматизированные оракулы для позитивных комбинаций тестов проверяют насколько исходящие условия или состояния соответствуют ожидаемым результатам, и не появляются ли такие неожиданности как сообщения об ошибках, необработанные исключения или других неисправности.
А не должны ли мы устроить проверку и для негативных значений? Это действительно хороший вопрос и я должен сказать, что не очень уверен в этом по двум причинам:
- большинство обработок эксепшенов является результатом ошибки только в одном режиме. Другими словами, давайте предположим, что у нас есть виндовые формы, содержащие 3 поля ввода для целых чисел, и вводимые данные не обрабатываются пока не нажата кнопка ОК или Apply. И давайте предположим, что мы вводим символ “А” в каждое поле ввода. Обычно когда юзер нажимает ОК или Apply эксепшен вызывается (и приложение отображает сообщение об ошибке) при обнаружении первого же неправильного значения. Если мы вводим несколько исключений, то программа либо не смотрит на повторяющиеся ошибки, либо пользователь получает каскад сообщений об ошибках.
- большинство ошибок не являются результатом сочетания неверных входных значений в различных комбинациях. Например, даже в случае нескольких веб-форм, которые проверяют несколько значений на ошибки, обычно значения не публикуются, пока пользователь не исправит введенные ошибочные значения во всех веб-формах. Даже в этом случае маловероятно, что несколько неправильных значений могут в результате привести к неожиданным ошибкам.
Итак, зададим себе три вопроса:
- Должны ли мы считать, что ввод неправильных значений в определенной комбинации с другими допустимыми входными значениями приведет к какому-либо неожиданному результату, который не будет обнаружен при использовании техник тест-дизайна, направленных на обнаружение одиночных ошибок?
- Должны ли мы предполагать, что нам необходимо интенсивно использовать тесты для различных комбинаций неправильных входных значений?
- Необходимо ли нам повторять такие тесты в продолжении всего жизненного цикла разработки продукта (SDLC)?
Это очень важные вопросы, поскольку если делать правильно и в правильном контексте, то комбинаторная техника входных значений получается более “дорогой” в сравнении с другими подходами к тестированию. Например, мы знаем, что комбинаторное тестирование может найти баги отдельных значений, но есть более эффективные (менее “дорогие”) пути найти такие ошибки. Тестирование комбинаций входных значений, которые оказывают влияние на общие выходные условия или состояния, определяется входными параметрами и вариациями этих параметров, моделированием входных данных, изучением исходящих, настройки модели, а в идеале: автоматизацией управляемых тестовых данных для тестов. Слишком трудоемко (“дорого”) проверять какие из неверных значений (например, символ “А”, введенный в поле, принимающее только целочисленные значения) дали нужный отклик (например, сообщение об ошибке). Еще один фактор: когда мы тестируем комбинацию негативных значений и появляется какая-то непонятная ошибка, то мы должны потратить некоторое время для выяснения - была ли аномалия вызвана этой комбинацией или это дефект какого-то одного из значений.
Но если на любой из этих вопросов ответ “да”, тогда мы можем использовать комбинаторный подход в тестировании. Конечно, включение недопустимых входных значений в нашу модель будет создавать негативные тесты в нашем базовом тестовом наборе, сгенерированном программой. В качестве примера давайте рассмотрим различные ожидаемые выходные значения для симуляции диалога шрифтов.
- если все вводимые значения допустимые, мы ожидаем отображения в нашем окне редактирования символов (на самом деле глиф), соответствующих вводимым значениям, и никаких сообщений об ошибках, непонятных эксепшенов и приложение не находится в статусе “не отвечает”.
- если значение цвета шрифта представляет собой любые недопустимые данные, тогда при нажатии кнопок ОК или Apply сообщение об ошибке не отображается, но цвет шрифта возвращается к последнему допустимому цвету шрифта (или к цвету шрифта по умолчанию).
- если размер шрифта меньше 1 или больше, чем 1638, появляется сообщение об ошибке и размер возвращается к последнему допустимому значению.
- если размер шрифта имеет десятичное значение, отличное от n.5, то появляется сообщение об ошибке (или значение может быть округлено до ближайшего целого или n.5 числа) и размер возвращается к последнему допустимому значению.
- если цвет шрифта имеет недопустимое значение и размер шрифта имеет недопустимое значение, тогда цвет возвращается к последнему допустимому значению и появляется сообщение об ошибке с недопустимым размером шрифта.
Итак, давайте рассмотрим два различных сценария: множество неправильных входных значений и отдельные неправильные значения в комбинации с допустимыми входными значениями.
Тестирование множества неправильных входных значений.
Комбинаторное тестирование утверждает, что при взаимодействии двух (или больше) входных значений возможны неожиданные состояния или выходные значения. Поэтому, раз мы считаем, что 2 или более недопустимых значения могут привести к неожиданной аномалии, тогда мы можем просто добавить недопустимые входные значения в нашу модель. Например, в нашем примере диалога выбора шрифтов мы можем вставить недопустимые входные значения для цвета и размера шрифта, как это проиллюстрировано в модели ниже (выделено жирным):
# Model File for MyFontDialog
Font: Arial(50), Tahoma, BrushScript, MonotypeCorsive
Style: Bold, Italic, BoldItalic, None(10)
Effects: Strike, Underline, StrikeUnderline, None(10)
Colors: Black(10), White, Red, Green, Blue, Yellow, Purple, Orange, randomString, emptyString
Size: small, smallHalf, nominal(10), nominalHalf, large, largeHalf, xLarge, xLargeHalf, xxLarge, xxLargeHalf, emptyString, integerLessThan1, integerGreaterThan1638, floatValueOtherThan.5
# Conditional constraints necessary to prevent mutually exclusive variable settings
# See previous post for dealing with mutually exclusive variables if [Font] = "BrushScript" then [Style] in { "Italic", "Bold/Italic" };
if [Font] = "MonotypeCorsive" then [Style] in { "None", "Bold/Italic" };
Набор тестовых комбинаций создается специальным инструментом, в который вводятся как позитивные, так и негативные тесты. Например, эта модель будет производить набор тестов, которые включают такие комбинации как цвет Color == emptyString с размером Size == emptyString, и цвет Color == Purple с размером Size == integerGreaterThan1638 с комбинациями значений для других входных параметров. Она также включает тесты типа: цвет Color == randomString с допустимыми входными данными для других параметров. В этом случае мы должны были бы пройти один за другим через весь набор тестов, произведенный нашим инструментом, и определять ожидаемые выходные значения для каждой комбинации. Такой подход был бы более практичным, если бы мы выполняли тестовые комбинации вручную и при этом оценивали результаты выполнения каждого теста. Но это будет очень длительный процесс и он потребует несколько комплексных оракулов, если мы захотим его автоматизировать.
Тестирование отдельных неправильных входных значений.
В некоторых случаях мы можем протестировать отдельно каждое неправильное значение входного параметра в комбинации c допустимым значением другого входного параметра, чтобы изучить конкретные ожидаемые состояния (например, появление ошибок). В таком случае мы должны определить неверные входные значения так, чтобы они не использовались в комбинации с другими недопустимыми значениями для других входных параметров. К счастью, PICT поддерживает такой тип анализа. В нашей модели входных данных мы можем выделить недопустимые значения значком тильды (~). Измененная модель файла ниже теперь включает недопустимые значения для параметров размера и цвета.
# Model File for MyFontDialog
Font: Arial(50), Tahoma, BrushScript, MonotypeCorsive
Style: Bold, Italic, BoldItalic, None(10)
Effects: Strike, Underline, StrikeUnderline, None(10)
Colors: Black(10), White, Red, Green, Blue, Yellow, ~randomString, ~emptyString
Size: small, smallHalf, nominal(10), nominalHalf, large, largeHalf, xLarge, xLargeHalf, xxLarge, xxLargeHalf, ~emptyString, ~integerLessThan1, ~integerGreaterThan1638, ~floatValueOtherThan.5
# Conditional constraints necessary to prevent mutually exclusive variable settings
# See previous post for dealing with mutually exclusive variables if [Font] = "BrushScript" then [Style] in { "Italic", "Bold/Italic" };
if [Font] = "MonotypeCorsive" then [Style] in { "None", "Bold/Italic" };
Вышеуказанная модель генерирует основной набор комбинаторных тестов, которые включают в себя все комбинации допустимых параметров, а также комбинации, включающие одно недопустимое значение в n-мерных комбинациях с другими допустимыми значениями, как показано на рисунке (файл с выходными значениями). Обратите внимание, что в каждом тесте присутствует только одно недопустимое значение. Такой подход позволяет намного легче решить нашу проблему с оракулами, потому что, как было описано выше, каждый тест создает ожидаемое исходящее состояние.
Но мы все еще должны определить ожидаемые исходящие результаты для каждого теста в нашем наборе комбинаторных тестов, сгенерированных инструментом PICT. К счастью, у PICT есть незадокументированная фича: инструмент позволяет указать ожидаемые выходные данные или состояния. Чтобы смоделировать ожидаемый результат, нам нужно просто включить параметр, начинающийся со знака доллара ($). Например, мы можем изменить нашу модель включив в него параметр “$Result” и присвоив ему ожидаемое выходное условие, как показано ниже.
# Model File for MyFontDialog
Font: Arial(50), Tahoma, BrushScript, MonotypeCorsive
Style: Bold, Italic, BoldItalic, None(10)
Effects: Strike, Underline, StrikeUnderline, None(10)
Colors: Black(10), White, Red, Green, Blue, Yellow, ~randomString, ~emptyString
Size: small, smallHalf, nominal(10), nominalHalf, large, largeHalf, xLarge, xLargeHalf, xxLarge, xxLargeHalf, ~emptyString, ~integerLessThan1, ~integerGreaterThan1638, ~floatValueOtherThan.5
# Expected Results Parameter
$Result: ErrorMessage, DefaultColor
# Conditional constraints necessary to prevent mutually exclusive variable settings
# See previous post for dealing with mutually exclusive variables
if [Font] = "BrushScript" then [Style] in { "Italic", "Bold/Italic" };
if [Font] = "MonotypeCorsive" then [Style] in { "None", "Bold/Italic" };
# Expected Outputs
if [Colors] in {"randomString", "emptyString"} then [$Result] = "DefaultColor";
if [Size] in {"emptyString", "integerLessThan1", "integerGreaterThan1638", "floatValueOtherThan.5" } then [$Result] =
"ErrorMessage";
Теперь в наши выходные значения PICT включил еще одну колонку для ожидаемых результатов (как на картинке). Обратите внимание, что в этом случае, если ожидаемый результат - это допустимые выходные значения (свойства шрифта соответствуют введенным значениям), колонка Result содержит знак вопроса (?). Если есть несколько ожидаемых состояний, то “допустимое” исходящее состояние будет отмечено знаком вопроса, как в этом примере.
Если ожидаемый результат бинарен (например, Error или NoError), мы можем использовать один вариант в нашей модели, как, например:
if [param] = “InvalidCondition” then [$Result] = “Error” else [$Result] = “NoError”;
Смысл параметра $Result в том, что мы можем использовать значения $Result как флаги при автоматизации тестов для переключения между различными оракулами автоматизации, чтобы помочь проверить указанный ожидаемый результат.
Такой подход моделирует как допустимые, так и с недопустимые входные значения, а инструмент PICT генерирует как позитивные, так и негативные выходные тестовые комбинации. Использование параметра $Result как флага является эффективным решением для переключения между автоматизированными оракулами и позволяет нам создавать отдельные автоматизированные тесты. Даже с таким подходом я задаюсь вопросом: вызывает ли неожиданную ошибку комбинация недопустимого значения с допустимым входным значением, или, может, это ошибка какого-то одного модуля. Но если я действительно не знаю как недопустимые входные значения проверяются перед передачей соответствующей функции, тогда включение недопустимых значений в нашу модель может также увеличить общее покрытие тестами и повысить нашу уверенность, или теоретически раскрыть несколько действительно случайных ошибок!