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

Введение

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

  • входные значения для тестируемого программного обеспечения
  • конкретная конфигурация аппаратного и программного обеспечения
  • перевод в определенное состояние
  • ожидание определенных результатов

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

Что такое идея по тестированию?

Идея по тестированию (или требование к тесту) - это краткое описание теста, который нужно выполнить. В качестве примера рассмотрим функцию, вычисляющую квадратный корень, и предложим несколько идей для ее тестирования:

  • дать в качестве входного значения число немного меньше нуля
  • дать в качестве входного значения ноль
  • дать в качестве входного значения квадрат целого числа, например 4 или 16 (будет ли результатом ровно 2 или 4?)

Эти идеи можно без труда преобразовать в исполняемые тесты с точным описанием входных значений и ожидаемых результатов.

Относительно неформальный промежуточный список идей обладает двумя преимуществами:

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

В примерах идей для функции вычисления квадратного корня использовались только входные значения, однако ничто не мешает использовать любые другие элементы исполняемого теста. Например, идея "напечатать на принтере LaserJet IIIp" описывает среду тестирования, равно как идея "выполнить тест с полной базой данных", хотя эти идеи не отличаются полнотой: что нужно напечатать на принтере? Что нужно сделать с полной базой данных? Однако эти идеи напоминают о том, что именно нельзя упускать из виду, позволяя отложить конкретную реализацию этих идей до момента разработки тестов.

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

double sqrt(double x) {
    if (x < 0) 
      // signal error
    ...

Вполне может быть, что вместо < по ошибке будет указано <=. Такие опечатки встречаются довольно часто, и поэтому они стоят проверки. Данную ошибку нельзя обнаружить при X, равном 2, поскольку как неправильное (x<=0), так и правильное выражение (x<0) в этом случае пойдут по одной и той же ветви оператора if. По этой же причине данную ошибку нельзя обнаружить, присвоив X значение -5. Единственный способ обнаружить эту ошибку заключается в том, чтобы присвоить X значение 0, и это обстоятельство оправдывает создание второго теста.

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

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

Эти модели сбоев применимы к разнообразным рабочим продуктам. Например, в первой приведены идеи по тестированию логических выражений. Логические выражении могут встречаться непосредственно в коде, в сторожевых условиях, циклограммах и диаграммах состояний, а также в неформальных описаниях поведения методов (например, в опубликованных API).

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

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

Проектирование тестов с помощью списка

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

int Collection.find(String string,
                    Boolean ignoreCase);

Вот несколько идей по тестированию данного метода:

  1. совпадение найдено в первой позиции
  2. совпадение найдено в последней позиции
  3. не найдено ни одно совпадение
  4. найдены несколько совпадений
  5. при поиске без учета регистра найдено совпадение, которое не было бы совпадением с учетом регистра
  6. при поиске с учетом регистра найдено точное совпадение
  7. при поиске с учетом регистра пропущено значение, которое было бы совпадением без учета регистра

Реализация этих семи тестов - по одному на каждую идею - не составила бы труда. Однако в одном тесте можно объединить несколько идей. Например, следующий тест удовлетворяет условиям идей 2, 6 и 7:

Подготовка: создается набор, содержащий значения ["dawn", "Dawn"]
Вызов теста: collection.find("Dawn", false)
Ожидаемый результат: значение 1 (если значение "dawn" не будет пропущено, метод вернет значение 0)

Чем более обобщенные формулировки идей, тем проще объединять их.

Предположим, что можно охватить все идеи тремя тестами. Чем три теста, охватывающие семь идей, лучше семи отдельных тестов?

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

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

    Чем лучше такой подход? Если бы список идей по тестированию был полным и идеи охватывали все возможные ошибки в программе, способ создания тестов не играл бы роли. Однако списки никогда не бывают полными. Чем разнообразнее тесты и чем больше различных участков кода они заставляют работать, тем больше вероятность того, что какая-либо ошибка будет выявлена случайно. В результате у меньшего количества сложных тестов больше шансов обнаружить ошибку, о которой вы даже не задумывались, чем у большего количества однообразных тестов.

  • Иногда при создании сложных тестов в голову приходят новые идеи. При работе с простыми тестами идеи приходят реже, потому что все тесты однообразны, и это усыпляет разум.

В то же время у сложных тестов есть и недостатки.

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

  • Сложные тесты менее понятны и требуют больших усилий по обслуживанию. Цели таких тестов менее очевидны.

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

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

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

Применение идей по тестированию перед тестированием

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

Изображение примера модели проекта

Рисунок 1: Связь между классами Department и Employee

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

Подобные недоработки, когда разработчик не учел какой-либо сценарий, называются упущениями. Если в проекте допущено упущение, скорее всего, оно же допущено и при разработке идей по тестированию. Примеры упущений рассматриваются в публикациях [GLA81], [OST84], [BAS87], [MAR00] и других работах.

Роль тестирования в разработке продуктов обсуждается в разделе Концепция: разработка с приоритетом тестов.

Идеи по тестированию и прослеживаемость

Прослеживаемость - это вопрос компромиссов. Достаточно ли пользы от нее, чтобы оправдать усилия на ее реализацию? Это вопрос нужно обдумать при выполнении задачи Задача: формулировка потребности в оценке и прослеживаемости.

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

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