среда, 1 октября 2014 г.

Коротко. Про тесты и пост- и пред-условия.

По мотивам - Коротко. "Почему нужны тесты".

Посмотрим на код теста:

Параметры: ( "Документ из базы {("Случайный документ")}" )
Выполнить (...)

-- тут есть ДВЕ части - Параметры и Выполнить.

Что есть что?

Параметры - это набор пост- и пред-условий.

Это не я придумал, и не я реализовал, но мне это очень нравится.

Ну а Выполнить - это собственно - код теста.

Хочу немного рассказать про пред- и пост-условия.

Что это такое?

Это инварианты, которые выполняются до и после выполнения кода теста.

И выполнение этих вариантов - гарантирует скриптовая машина.

Если инвариант, для предусловия - не выполняется, то тест даже не запускается.
Если инвариант, для постусловия - не выполняется, то последующие тесты не запускаются. (В реальности не совсем так, то мысль именно в этом)

Что написано тут:

Параметры: ( "Документ из базы {("Случайный документ")}" )

?

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

А что до постусловий?

Как они выглядят?

Да так же.

Параметры: ( 
 "Документ из базы {("Случайный документ")}" 
 "Закрывать все открытые окна"
)

Тут - "Документ из базы {("Случайный документ")}" это наше знакомое предусловие.

А вот - "Закрывать все открытые окна" - это постусловие.

Оно означает, что после выполнения теста все открытые окна приложения будут закрыты.
И это сделает не "код теста", а скриптовая машина. Независимо от того - прошёл тест или нет.

Мне не очень нравится, что предусловия и постусловия тут синтаксически неразличимы.
Т.е. предусловие это или постусловие - это зависит от реализации.

Меня лично это смущает. Но пока - так сделано. В реальности.

В будущем (наверное) стоит сделать так:

Предусловия: ( 
 "Документ из базы {("Случайный документ")}" 
 ...
 "Если документ уже был открыт, то закончить тест с успехом" // - Это полезно для много-процессных тестов, которые конкурируют за ресурс
)
Постусловия: (
 "Закрывать все открытые окна"
 ...
 "Изменяемый документ должен совпасть с эталоном" // - это крайне полезно, если мы в тесте меняем документ и хотим проконтроллировать корректность изменений и сохранения
 ...
 "Сбрасывать базу в исходное состояние"
)
Выполнить: (
 // - Тут код собственно теста
)

-- так должно бы быть, но это "пока мечты".

Но мне кажется, что кому-то идея с пост- и пред-условиями - понравится.

Конечно же пост- и пред-условия это некая замена конструкциям try..finally и try..except, на (на мой взгляд) - гораздо более читабельная и самодокументируемая.

Как это всё устроено?

Предусловия - это "массив функторов", которые вызываются до теста.

Ну и постусловия - это "массив функторов", которые вызываются после теста.

При этом код "кишков скриптовой машины" выглядит примерно так:

for Предусловие in Предусловия do
 try
  Если НЕ Предусловие то
   raise 'Тест не прошёл из-за предусловий'
 except
  raise 'Тест не прошёл из-за предусловий'
 end;

BOOLEAN VAR "Тест прошёл"
"Тест прошёл" := ДА
try
 try
  "Код теста"
 except
  "Тест прошёл" := НЕТ
 end
finally
 BOOLEAN VAR "Всё хорошо"
 "Всё хорошо" := ДА
 for Постусловие in Постусловия do
  try
   Если НЕ Постусловие то
    "Всё хорошо" := НЕТ
  except
   "Всё хорошо" := НЕТ
 end;
 Если НЕ "Тест прошёл" то
  raise 'Тест не прошёл сам по себе'
 Если НЕ "Всё хорошо" то
  raise 'Тест не прошёл из-за постусловий'
end

-- ну как-то так..

P.S. Вот кстати "параллельное мнение" (надеюсь, что автор не обидится на меня):

"Предусловия и Постусловия сделать - нет проблем. Но, если уж следовать
логике, то лучше бы их реализовать так:
Предусловия: (
)
Выполнить (
)
Постусловия: (
)
А вот это технически (на уровне скриптов) сейчас нереализуемо. Я,
по-началу, развлекался возможностью задавать Параметры: до Выполнить и
после. Но, оказалось, что не работает так. Пришлось эту возможность
вычеркнуть.
Но это, повторюсь, текущая проблема, которая при заточке машинки может
быть легко решена.
Теперь дополнения и замечания. Параметры создавались с основной задачей -
упростить код теста. Убрать оттуда все ненужное, чтобы это все было "за
кадром". Т.е. чтобы код теста (в Выполнить) максимально соответствовал
тексту в ошибке/задаче. Это был основной постулат. Плюс очередная попытка
упростить понимание тестов. Идея в том, что "в кишки", коих уже написано
много до поры до времени тестировщик не лезет. А поддерживает их
программист/более опытный тестировщик. Но, понятно, что в нашем случае
получилась очередная утопия (пришел "Новый незамутнённый тестировщик" и влез "в кишки", а параметры
ничем ему не помогли).
Мне не очень нравится слова постусловия и предусловия и вот почему.
Параметры задумывались с целью уменьшить/упростить код теста. Это, по возможности,
отглагольные существительные (передаю привет стандарту IDEF0), указывающие на действия, которые будут
сделаны в тесте. Их основное назначение - краткость. Как в Паскале есть
begin, а в Cи "{" на не "begin inner block" или что-то подобное. Поэтому,
вот такая запись: "Если документ уже был открыт, то закончить тест с
успехом" мне режет глаза. Кстати, вот эта тоже - "Документ из базы
{("Случайный документ")}". По мне, так лучше "Случайный документ" и все.
Еще не забываем параметры по умолчанию. Их цель - убрать необходимость
писать все параметры во всех тестах. На некоторых тестах ничего писать не
нужно. Для этого существуют параметры по умолчанию и возможность опускать слово "Параметры:".
Есть у меня идеи по связыванию пред и постусловий (частично я их уже начал реализовывать). Т.е. если вставлен "Случайный
документ", то автоматически выставляется "Закрыть документ". Если были
операции с базой, то автоматически выставляется параметр "Очистка базы".
Зачем? Чтобы "чей-нибудь склероз" не разнес все тесты нафиг. А здесь
появляются два момента. Первый,  когда нужен какой-нибудь отладочный
режим. Т.е. доводим до какого-нибудь момента и ничего не закрываем. Такой режим должен учитываться.
Но тут отдельная тема для размышлений и, пока, не об ней речь.
Второй, все равно нужен контроль используемых ресурсов.
Выставили/невыставили параметр или где-нибудь в "Выполнить" что-то с базой
сделали - должна быть на выходе проверка, что база изменилась. И вот здесь
появляется новое понятие - проверяющие слова. Если слово обнаружило, что
параметра не было, а база изменилась, то она должна также выдать
Error/Warning и базу почистить. Но тогда, если есть такие слова (они,
кстати, будут внутренним понятием, вряд ли нужно их выпускать наружу)
смысл в постусловиях, частично, теряется. Они фактически выставляют флаги,
не более того. Другое дело - проверки теста. Вот их нужно задавать в
постусловиях.
Да, "Если документ уже был открыт, то закончить тест с успехом" в текущие
параметрические тесты не вписывается. Нет там сейчас такой обработки.
Досрочное окончание теста не предполагалось. Надо бы над этим подумать...
И последнее замечание, параметры (предусловия/постусловия) - это не только замена try except end,
а ближе к препроцессорной обработке. Они как замена условной компиляции. Хотя, наверно, несколько более широкое понятие. Текущая реализация - это не более, чем временное техническое ограничение. Его пока не требуется преодолевать, т.к. не накопилось достаточной массы задач, которым требуется что-то большее."

1 комментарий:

  1. Термины "предусловия" и "постусловия" ввел, насколько я помню, Дейкстра в книге "Дисциплина программирования".
    И означали они условия математические. Предикаты.
    Введены они были для строго--математического построения алгоритмов. Исходные данные подчиняются предусловиям, а результаты - постусловиям. Алгоритм должен гарантировать, что если исходные данные подчиняются предусловиям, то результаты будут подчиняться постусловиям.
    Более подробно идеология математического вывода алгоритмов описана в учебнике Д.Гриса "Наука программирования".
    Я это к чему? К тому, что у Вас предусловия и постусловия используются в некотором, гораздо более широком смысле.
    Это, скорее, пред- и постоперации.

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