вторник, 30 апреля 2013 г.

Продолжение о тестах

Первая серия была тут - http://18delphi.blogspot.com/2013/04/blog-post_5863.html

Пишет мой коллега:


Как справедливо замечено, есть действительно 2 основных направления автоматизированного тестирования: проверять фиксированные сценарии (в интерфейсе) или просто следить за тоннами активизируемого контролами функционала. Каждое имеет свои преимущества и недостатки. Но на данном этапе было принято решение проверять только сценарии уже найденных и написанных ошибок. И это, на мой взгляд, разумно, т.к. ошибки эти  находились в разных прецедентах системы. Каждый новый написанный тест охватывает небольшую толику функционала. Но этих тестов становится все больше. И чем их больше, тем выше вероятность, что ошибка будет не пропущена. Для поиска ошибок с сильно ограниченными трудовыми ресурсами – это оптимальный вариант.
Таким образом, избегая зацикливания на одном объекте системы, тесты охватывают всю систему паутиной. И стоит “задеть” одну нить – вся паутина задрожит и даст знать, что что-то случилось. С одной стороны, это замечательно, что тесты перестали проходить. Значит, они работают и нужно проверить изменения в системе. С другой, это ужасно неудобно с точки зрения локализации проблемы. Очень редко, ну очень, возвращается старая ошибка. Тест специально заточен под то, чтобы аккуратно зафиксировать ошибку и уведомить об этом тестировщика. Но, как правило, ломается что-то другое, “по-соседству”. Тест это честно находит, но часто не может четко показать причину ошибки. На эти случаи вставляются ASSERT ы, но всех возможных ошибок нельзя учесть (тогда написание одного теста будет занимать очень долгое время).
Сами тесты я разделяю на следующие группы:
- Уникальные тесты (которые никогда не пересекаются с другими тестами и проверяют область, которую не проверяют другие тесты);
- Тесты, на которые могут повлиять другие тесты (и это тоже проблема, которая решается “качественным” кодом). Например, любые тесты с поиском (от другого теста может остаться фильтр, который изменит результат).
- Тесты – близнецы (разница в которых может составлять и одну строку, и один символ).
Значит, тесты можно делать либо максимально универсальными, либо максимально уникальными. Но в сложных программах путь универсализации – ошибочен. Первое время я старался писать тесты и слова для них с запасом для будущих тестов. Специально опускал некоторые части кода. Например:

: “Выделить весь текст в документе”
“Зайти в документ”
“Выделить весь текст”
“Сравнить выделенный текст с эталоном”
; \\ “Выделить весь текст в документе”

То есть, я знаю, что при входе в документ фокус стоит в тексте. И опускаю код “Установить фокус в текст”. Значит, в этом тесте проверяется не только результат выделения, но и то, что фокус оказывается в документе (косвенно). Но знаю также, что есть другой тест, который проверяет именно то, где находится фокус:

: “Проверить, что фокус после открытия документа находится в тексте”
“Зайти в документ”
“Проверить, в каком контроле стоит фокус”
; \\“Проверить, что фокус после открытия документа находится в тексте”

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

: “Выделить весь текст в документе”
“Зайти в документ”
“Установить фокус в текст”
“Выделить весь текст”
“Сравнить выделенный текст с эталоном”
; \\ “Выделить весь текст в документе”

  Такой подход, в будущем, упростит разбор ошибок. Т.е. стараться сделать каждый тест уникальным, проверяющим только описанную ошибку – правильно. К этому нужно стремиться, но без фанатизма.
Теоретически, если все тесты будут работать по такому принципу, не нужен будет специальный человек, который обучен работать с тестами. Потому что в каждом непрошедшем тесте будет подробно описана ошибка. Другие тесты пройдут успешно. Любой пользователь сможет написать ошибку, приведя номер теста и сообщение найденной ошибки. Но это – идеальная система. Добиться, такого результата, думаю, невозможно. Хотя идти к нему стоит.
Если рассматривать мою работу, с точки зрения результативности, то я бы оценил её как среднюю. Ошибки исправно находятся, но есть несколько причин, по которым её нельзя назвать хорошей.
Во-первых, это малое количество тестов, из-за чего вероятность пропустить ошибку очень высока. Чтобы убрать эту причину, нужно лишь время (очень много времени).
Во-вторых, авторство всех тестов принадлежит трем людям. Иногда (в последнее время всё реже) приходится разбираться, почему же тест написан так, когда проще было сделать по-другому. Это частично связано с тем, что ранний функционал не обеспечивал простоту и понятность кода. А также, есть свои “секреты” в работе системы (и тестового приложения). Конечно, очень полезно расставлять комментарии в коде именно ради таких моментов. Но каюсь, добавляю комментарии редко, держу особенности работы в голове. Иногда они забываются, но если с проблемой уже сталкивался, вспоминается она очень быстро. Т.е. коду не хватает формализации.
В-третьих, человеческий фактор. У нас налажена тройная проверка готового теста: первый раз его проверяет автор при отладке, далее тест смотрит разработчик и, если все работает корректно и проверяет, что нужно, тест попадает опять к автору на последнюю проверку и закрытие. При такой проверке маловероятна возможность пропустить ошибку в самом тесте, но есть возможность самим кодом проверять не совсем верный сценарий (не повторяющий ошибку, т.к. тесты пишутся и для уже исправленных ошибок). Бороться с этой причиной проще остальных. Нужно лишь прилагать максимум внимания и стараться выбросить мысли о других ошибках из головы. Полезно также посмотреть точный сценарий ошибки на сборке, на которой она воспроизводилась.
В-четвертых, можно ли утверждать, что когда-то написанный и отлаженный тест будет корректно проверять именно то, что нужно, через 2-3 года. Нельзя. Нужно иногда проводить ревизию тестов (хотя бы выборочно).
Есть еще несколько малозначимых причин. Их, думаю, можно не указывать.
Теперь рассмотрим, какой же человек может справиться с работой с автоматическими тестами.
1) Он должен знать термины и аббревиатуры, принятые в системе.
2) Нужны базовые знания любого языка программирования (не обязательно Delphi или FORTH).
3) Нужна аккуратность, вдумчивость и усидчивость.
То есть, критериев для кандидата не так уж и много. Но их можно сделать еще меньше, упростив работу по написанию тестов. Сделать это очень просто.
Приведу пример из жизни. Однажды мне пришлось поработать в Тверьстате (Территориальный Орган Федеральной Службы государственной статистики). Первая половина работы не имеет к данному рассказу никакого отношения (контроль и правка результатов сканирования). Зато вторая стала очень поучительной. Её суть заключалась в правке Excel – документов (оптимизировать размер полей под печать на минимальном количестве листов). Задача очень простая. Но однотипных документов были тысячи, и выполнять все действия вручную было несерьезно. Была нужна автоматизация. И она была создана одним очень талантливым студентом.
Процесс нашей работы имел вид:
1 день) Ознакомились с заданием, вручную сделали несколько документов.
2 день) Кто-то вспомнил, что в Excel есть макросы. Создав один макрос, его можно указывать из любого документа. Исправили несколько сотен документов.
3 день) В. Е. написал программу, для работы которой нужно: указать папку с документами, указать путь к документу с макросом и его имя, и нажать кнопку Пуск. Программа открывает по порядку документы из папки, применяет макрос, сохраняет документ и закрывает его. Вся работа свелась созданию одного макроса в начале рабочего дня. Не понимаю, почему штатный состав программистов не создал для Тверьстата подобную программу много лет назад (мы были практиканты).
Но речь сейчас не об этом, а о макросах.  Пользователь понятия не имеет какой код используется, когда он записывает сценарий. Он лишь выполняет конечную последовательность действий, которую ему необходимо неоднократно выполнить. Для этого не нужны сверхзнания или специальная подготовка, достаточно будет лишь терминов системы.
То есть, на мой взгляд, написание основной группы тестов должно рано или поздно свестись к работе, схожей с написанием макросов: выполняем действия, система их запоминает и в конце сравнивает результат по каким-либо критериям. Это сделает написание тестов более простым и гораздо более быстрым. Если потребуется написать тест под каждое требование из ТЗ или автоматизировать проверку уже созданных тесткейсов – данный подход поможет это сделать. Ручное написание подобного количества тестов заняло бы годы.
Задачей разработчиков здесь бы стало максимально формализовать код (который записывает алгоритм действий) и результат работы тестов, а также подготовить специалиста, который бы разбирал результаты прогона тестов. Также, этот человек должен будет заниматься написанием более сложных тестов, в которых не получается работать с четким алгоритмом.
Не уверен, что на данном этапе такую систему можно реализовать. Также не уверен, что она окажется идеальной по поиску ошибок и гибкой (при ее использовании). Но данную идею можно додумать. И тогда она может оказаться весьма эффективной.


Трофимов Андрей

Комментариев нет:

Отправить комментарий