Ещё раз хочу повторить вот что:
1. тест должен быть линейным
2. похожим на тест-кейс
3. читаться человеком
4. оперировать терминами предметной области
К чему это я?
Я сегодня просматривал код. Много разного кода. И некоторые вещи заставили меня задуматься о том, что есть вещи которые видимо стоит повторять неоднократно.
Итак.
Повторю ещё раз:
1. тест должен быть линейным
2. похожим на тест-кейс
3. читаться человеком
4. оперировать терминами предметной области
Для начала хочется сказать о линейности.
Предположим, что мы пишем тест на Паскале.
Так вот вот это:
-- не самый лучший тест.
Тут дело в следующем - если вдруг возникает необходимость в условном операторе, то скорее всего мы имеем дело со следующими случаями:
1. Этот тест на самом деле с точки зрения пользователя распадается на два теста (сценария) - когда кнопка нажата или когда она не нажата.
Тут надо понимать вот какую вещь - пользователь хочет сделать что-то, что он задумал. А не стоит перед выбором. И ожидает что кнопка не нажата. Тогда это его нормальный сценарий.
А вот если она вдруг нажата - это означает, что "что-то пошло не так". Пользователь не ожидал этого. И значит у него "глаза на лоб полезли". Он судорожно пытается сообразить "что-же делать". Т.е. мы явно имеем дело с двумя сценариями взаимодействия пользователя и системы.
А следовательно надо писать - два или более тестов. Ну мне так кажется.
2. Проблема вся в том, что мы имеем дело с "ненастроенной системой" или с неправильными данными.
Тогда тест скорее всего надо писать так:
- т.е. тест в данном случае однозначно не должен проходить.
Потому, что систему в "нестабильном состоянии" - нет смысла тестировать каким-либо образом, кроме как проверить её состояние.
3. Самый сложный для понимания случай. Т.е. он - прост вроде бы.
Но вот способ написания теста для него - неочевиден.
Итак - случай такой. Мы на самом деле имеем дело с последовательностью автоматических тестов, которые так или иначе могут "портить состояние системы".
И тесты написаны так, (ну или система ведёт себя так), что мы не можем обеспечить детерминированное поведение.
Т.е. скажем в результате прогона предыдущих тестов случилось ток, что какие-то данные - "залипли" или какие-то настройки пришли в состояние, которое вообще говоря - ненормально, но с некоторой вероятностью - ожидаемо.
Тогда тест надо бы написать так:
А "под капотом" или "за ширмой" мы имеем всё то же:
Вроде то же самое... "В чём разница спросите вы" и будете правы.
Попробую объяснить.
Вернёмся вот к каким тезисам - "читаться человеком" и "похожим на тест-кейс". В варианте с If в теле теста - имеем вот что - человек который прогоняет тест руками прочитав этот If будет считать, что ему необходимо "убрать с кнопки SomeButton состояние Checked".
Однако это не так. При тестировании руками на "неразваленной" системе этот случай как раз-таки говорит о том, что "что-то пошло не так" и что не надо продолжать ручное тестирование, а надо начать процесс разбирательства - "а как же так вышло".
Этим ручное тестирование и отличается от автоматического.
Именно поэтому там написано MakeSure. Убедиться что это так.
Что же касается автоматических тестов - не что же - никто не совершенен. И из-за того, что сломались предыдущие тесты и привели систему в нестабильное состояние - это не повод - не продолжать прогонять последующие тесты. Может быть они что ещё интересного найдут.
А записи в логе - по-моему - вполне достаточно, чтобы получить сигнал о проблеме.
Тут тонкая грань. На уровне "декларативность" против "императивности". Но надеюсь, что я смог её для вас прояснить.
Это вот совсем не из разряда - "верьте мне". Нет. Просто - захотелось поделиться.
1. тест должен быть линейным
2. похожим на тест-кейс
3. читаться человеком
4. оперировать терминами предметной области
К чему это я?
Я сегодня просматривал код. Много разного кода. И некоторые вещи заставили меня задуматься о том, что есть вещи которые видимо стоит повторять неоднократно.
Итак.
Повторю ещё раз:
1. тест должен быть линейным
2. похожим на тест-кейс
3. читаться человеком
4. оперировать терминами предметной области
Для начала хочется сказать о линейности.
Предположим, что мы пишем тест на Паскале.
Так вот вот это:
OpenDocument(DocumentNumber); if (SomeButton.Checked) then SomeButton.Checked := false; DoOurWork;
-- не самый лучший тест.
Тут дело в следующем - если вдруг возникает необходимость в условном операторе, то скорее всего мы имеем дело со следующими случаями:
1. Этот тест на самом деле с точки зрения пользователя распадается на два теста (сценария) - когда кнопка нажата или когда она не нажата.
Тут надо понимать вот какую вещь - пользователь хочет сделать что-то, что он задумал. А не стоит перед выбором. И ожидает что кнопка не нажата. Тогда это его нормальный сценарий.
А вот если она вдруг нажата - это означает, что "что-то пошло не так". Пользователь не ожидал этого. И значит у него "глаза на лоб полезли". Он судорожно пытается сообразить "что-же делать". Т.е. мы явно имеем дело с двумя сценариями взаимодействия пользователя и системы.
А следовательно надо писать - два или более тестов. Ну мне так кажется.
2. Проблема вся в том, что мы имеем дело с "ненастроенной системой" или с неправильными данными.
Тогда тест скорее всего надо писать так:
OpenDocument(DocumentNumber); Assert(SomeButton.Checked); DoOurWork;
- т.е. тест в данном случае однозначно не должен проходить.
Потому, что систему в "нестабильном состоянии" - нет смысла тестировать каким-либо образом, кроме как проверить её состояние.
3. Самый сложный для понимания случай. Т.е. он - прост вроде бы.
Но вот способ написания теста для него - неочевиден.
Итак - случай такой. Мы на самом деле имеем дело с последовательностью автоматических тестов, которые так или иначе могут "портить состояние системы".
И тесты написаны так, (ну или система ведёт себя так), что мы не можем обеспечить детерминированное поведение.
Т.е. скажем в результате прогона предыдущих тестов случилось ток, что какие-то данные - "залипли" или какие-то настройки пришли в состояние, которое вообще говоря - ненормально, но с некоторой вероятностью - ожидаемо.
Тогда тест надо бы написать так:
OpenDocument(DocumentNumber); MakeSureThatButtonIsNotChecked(SomeButton); DoOurWork;
А "под капотом" или "за ширмой" мы имеем всё то же:
procedure MakeSureThatButtonIsNotChecked(aButton); begin if aButton.Checked then begin OutToLog('a button' + aButton.Name + 'was expected to be unchecked'); aButton.Checked := false; end; end;
Вроде то же самое... "В чём разница спросите вы" и будете правы.
Попробую объяснить.
Вернёмся вот к каким тезисам - "читаться человеком" и "похожим на тест-кейс". В варианте с If в теле теста - имеем вот что - человек который прогоняет тест руками прочитав этот If будет считать, что ему необходимо "убрать с кнопки SomeButton состояние Checked".
Однако это не так. При тестировании руками на "неразваленной" системе этот случай как раз-таки говорит о том, что "что-то пошло не так" и что не надо продолжать ручное тестирование, а надо начать процесс разбирательства - "а как же так вышло".
Этим ручное тестирование и отличается от автоматического.
Именно поэтому там написано MakeSure. Убедиться что это так.
Что же касается автоматических тестов - не что же - никто не совершенен. И из-за того, что сломались предыдущие тесты и привели систему в нестабильное состояние - это не повод - не продолжать прогонять последующие тесты. Может быть они что ещё интересного найдут.
А записи в логе - по-моему - вполне достаточно, чтобы получить сигнал о проблеме.
Тут тонкая грань. На уровне "декларативность" против "императивности". Но надеюсь, что я смог её для вас прояснить.
Это вот совсем не из разряда - "верьте мне". Нет. Просто - захотелось поделиться.
+1 Буду следить за Вашими заметками и здесь :)
ОтветитьУдалить