суббота, 14 января 2012 г.

Testing-by-contract VS defensive testing

Имеется у нас приложение. Для регистрации надо указать возраст.

До 12 лет - отказать в регистрации.
От 12 до 18 лет - дать доступ, но с ограничениями.
Больше 18 лет - полная регистрая, полный доступ к сайту.

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

Классы эквивалентности гласят, что нам нет смысла проверять все значения от 0 до 100 (бывают же долгожители). Мы можем взять по одному значению из каждого множества. Возьмем 5, 16 и 22. Все? Как насчет значений "10000", "-88", "%;№№"/"? Будем проверять?

Конечно же - "ДА!". Скажут многие. И будут по-своему правы.

Но знают ли они, что применяют именно "defensive testing"? Что есть и другой подход?

Далее идет вольный перевод Lee Copeland – “A Practitioner's Guide to Software Test Design”

Существует такой подход - design-by-contract. Под контрактом мы понимаем некие соглашения между частями, описывающими, что мы будем и не будем делать. В этом подходе методы описываются с помощью пред-условий и пост-условий. Пост-условие (post-condition) - это то, что модуль обещает сделать (открыть файл, сохранить значения, одобрить или отклонить регистрацию). Пред-условия (pre-condition) описывают требования к модулю, при которых он переходит в состояние, описываемое пост-условиями.

Например: функция, открывающая файл.
Post-condition: открыть файл
Pre-conditions: открываемый файл должен существовать, содержать нужное нам имя, быть "openable"...

Testing-by-contract основывается на философии design-by-contract. При использовании данного подхода мы пишем только те тест-кейсы, которые удовлетворяют нашим pre-condition. То есть на открытие файл мы НЕ пишем тест-кейс аля "файл не существует". Мы не создаем негативные кейсы, только позитивные.

У многих тестировщиков этот подход вызывает протест. Да еще какой! Как же так, а как же быть с некорректными условиями, а? Вы что?! Это очень важно!!

Да, важно! И для этого существует другой подход - defensive testing. Мы проверяем, что если условия удовлетворяют нашим pre-condition, то выполняет корретное пост-условие. Если же условия противоречат нашим pre-condition, то выдается корректное же сообщение об ошибке.

Таким образом, прежде чем ответить на поставленный вопрос: "Как насчет значений "10000", "-88", "%;№№"/"? Будем проверять?", вначале надо уточнить, а какой подход используется:

Если testing-by-contract - то ответ "Нет".
Если defensive testing - то ответ "Да".

А какой подход используете вы? :)
У меня первый подход вызывает протест. Как же так, забить на то, что вместо корректного сообщения об ошибке пользователь видит... Страшные вещи, порою... Тем более что пользователи довольно часто забивают неверные значения = им надо показывать корректные сообщения об ошибке. Иначе зачем им вообще нужна такая система, которая разваливается при каждом чихе? "Шаг влево, шаг вправо - расстрел" (с)

Нет, мы лучше обработаем возможные ошибки нормально - "Уважаемый юзер! Вам ну никак не может быть минус 93 года. Перепроверьте, пожалуйста, введенные вами данные и научитесь лгать поправдоподобнее. Спасибо за внимание, искренне ваш, Админ"

13 комментариев:

  1. Хм, второй раз приходит отчет о комменте, но в блоге его нет. То ли автор удаляет, то ли блог плющит :)

    testerпрокомментировал(а) ваше сообщение "Testing-by-contract VS defensive testing":

    они же - позитивные и негативные тесты :)

    > А какой подход используете вы? :)
    Полагаю, на практике большинство используют оба подхода.
    Просто их цели немножко разные.

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

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

    Понятно, что прямые сценарии/позитивные тесты/tests-by-contract - маст хэв по определению. Если они не работают, то приложение пользователю не нужно вообще.
    С другой стороны, defensive тестов обычно на порядок больше, чем by-contract. Но приоритет у них зачастую не так уж высок :)

    То есть tests-by-contract - это почти всегда P1 тесты.
    А defensive tests могут быть и P1, и P2, и Pn, где n зависит от принятых на проекте уровней приоритетности :).

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

    ОтветитьУдалить
    Ответы
    1. > они же - позитивные и негативные тесты :)
      Ну, в целом - да!

      Но врядли многие знают их англоязычные названия :))

      Согласна в том, что позитивные сценарии - это то, с чего надо начинать. Порою так увлекаешься негативом, что забываешь о позитиве. Это если исследуешь новую сборку, пока без конкретных тест-кейсов. Поэтому, как говорится "перед тем как тестировать - сядь и ПОДУМАЙ!" что ты будешь тестировать :)

      Удалить
    2. вероятно в спам упало )

      Удалить
  2. А иногда мне кажется, что тренеры слишком заморочены на классах эквивалентности в "testing-by-contract".

    "Классы эквивалентности гласят, что нам нет смысла проверять все значения от 0 до 100"

    - А сколько у нас времени? Если 1 минута на каждую проверку(по годам) и у нас 2 часа - то мы успеем проверить 100 лет.
    - А еще мы можем написать автоматический тест и он проверит 30 000 значений - каждый день каждого года(если вводим дату рождения). Сервера нам не жаль, хех.

    ОтветитьУдалить
  3. Ну, извратиться с автоматическим тестом - это одно, а проверять все 100 значений, даже если время есть...
    Бессмысленно, хоть есть время, хоть нет его. Классы эквивалентности, граничные значения, некорректные значения - так лучше. А сэкономленное время всегда можно найти куда потратить - почитать ТЗ на другой модуль, написать тесты, автотесты, поизучать нагрузочное и тд и тп

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

    Получается, что первоначально идет testing by contract, а потом мы плавно переходим к defensive testing.

    ОтветитьУдалить
  5. Это само собой.
    Хотя, если позитивных тестов много (модуль большой), можно прогнать пару негативных = дать работу программистам и досматривать себе позитив. Потом негатив :)

    Я вообще к тому, что мало кто знает "оригинальные" названия этих методов, хотя и знают их суть :)

    ОтветитьУдалить
  6. как по мне какое-то очень уж искусственное разделение

    ОтветитьУдалить
  7. Как-то необоснованно :)
    Почему? Какое вам больше нравится?

    ОтветитьУдалить
  8. tester прокомментировал(а) ваше сообщение "Testing-by-contract VS defensive testing":

    вероятно в спам упало )


    Хммммм, а как мне тебя оттуда достать? о_О

    ОтветитьУдалить
  9. Начала писать Вам ответ-комментарий, но вышло слишком объемно. Поэтому мой комментарий здесь.

    ОтветитьУдалить
  10. "Мы можем взять по одному значению из каждого множества. Возьмем 5, 16 и 22. Все?"
    Вы забыли про граничные значения: 12, 18, 0.

    ОтветитьУдалить
  11. Нет, я не забыла.
    Мы ведь говорили именно о классах эквивалентности. По ним я примеры и взяла.
    Граничные значения - это уже другая тема. Ее осваивают после классов эквивалентности :)

    ОтветитьУдалить