- PVSM.RU - https://www.pvsm.ru -
Как естественное продолжение работы над libuniset2 [1], возник проект uniset2-testsuite [2]. Это свой небольшой велосипед для функционального тестирования. В итоге он развился до более-менее универсального решения с «плагинами». Написан на python. Если интересно почитать, то прошу… заходите.
Основная идея тестирования, заложенная в uniset2-testsuite [2] проста: «Подали воздействие, проверили реакцию». Эта абстрактная идея материализовалась в итоге в следующие артефакты:
Тестовый сценарий — это xml-файл, в котором записывается последовательность тестов. Каждый тест в свою очередь делится на «действия» (action) и проверки (check). Тесты могут вызывать другие тесты, и, более того, можно вызывать тесты, находящиеся в других файлах. Всё это позволяет строить довольно сложные и развесистые последовательности тестов, общие части выносить в отдельные файлы, вызывать их «как процедуры» и т.п. Помимо этого поддерживается механизм замен (replace), позволяющий писать шаблоны тестов с некоторыми абстрактными названиями «переменных», которые подменяются на конкретные в момент вызова теста.
Поддерживается три вида действий:
Тут особо пояснять нечего. На примерах ниже будет видно.
Тут, я думаю, тоже более менее тоже всё очевидно.
В итоге простой тестовый сценарий имеет следующий вид:
<?xml version = '1.0' encoding = 'utf-8'?>
<TestScenario>
<Config>
...
</Config>
<TestList type="uniset">
<test name="Processing" comment="Проверка работы процесса">
<action set="OnControl_S=1" comment="Подаём команду 'начать работу'"/>
<check test="CmdLoad_C=1" comment="Подана команда 'наполнение'"/>
<check test="CmdUnload_C=0" comment="Снята команда 'опустошение'"/>
<check test="Level_AS>=90" comment="Цистерна наполняется.." timeout="15000"/>
<check test="CmdLoad_C=0" comment="Снята команда 'наполнение'"/>
<check test="CmdUnload_C=1" comment="Подана команда 'опустошение'"/>
<check test="Level_AS<=10" comment="Цистерна опустошается.." timeout="15000"/>
</test>
<test name="Stopped" comment="Проверка остановки процесса">
<action set="OnControl_S=0" comment="Снимаем команду 'начать работу'"/>
<check test="CmdLoad_C=0" comment="команда 'наполнить' не меняется" holdtime="3000"/>
<check test="CmdUnload_C=0" comment="команда 'опустошить' не меняется" holdtime="3000"/>
<check test="Level_AS<=80" comment="Уровень не меняется" holdtime="10000"/>
</test>
</TestList>
</TestScenario>
Как видно из приведённого примера. Действия записываются в виде тега
<action set="..."/>
проверки записываются в виде
<check test=".."/>
Раз есть сценарий, значит кто-то его должен исполнить. Поначалу было желание сделать систему, позволяющую написать свои «проигрыватели», у каждого из которых мог бы быть свой формат сценария. Но в итоге т. к. в качестве формата был выбран xml, то соответственно был написан uniset2-testsuite-xmlplayer. Консольный проигрыватель, который исполняет xml-сценарии. А дальше дело не пошло, потому-что xml-формат оказался достаточно удачным и xmlplayer-а хватает для решения всех текущих задач. Хотя в самом репозитории [2] есть ещё uniset2-testsuite-gtkplayer это GUI-проигрыватель, написанный на python-gtk. Но его развитие в какой-то момент было заморожено за ненадобностью и он не поддерживает многих функций.
Итого у нас получилось, что есть сценарии, состоящие из действий и проверок и есть проигрыватель который исполняет сценарии. Если всё это запустить, то на выходе можно увидеть примерно такую картинку
Немного предыстории…
В начале uniset2-testsuite рассматривался исключительно в рамках работы с libuniset [1], в которой основная концепция — «всё есть датчик». Поэтому формат записи подразумевает оперирование идентификаторами датчиков (числовыми или именами) и проверкой только числовых значений (целочисленных). Но постепенно развиваясь, добавилась возможность взаимодействия по протоколу modbus (type=«modbus»), недавно добавилась возможность работы через snmp (type=«snmp»). И в итоге стало более очевидно, что интерфейс для тестирования можно свести всего к двум основным функциям GET и SET. Т.е. для тестирования какой-либо системы достаточно реализовать всего две функции get_value(id) и set_value(id, value). И протокол взаимодействия с тестируемой системой не важен. Просто разработчик предоставляет свою реализацию этих двух функций для взаимодействия со своей системой. Эта идея легла в основу создания аналога системы плагинов. Т.е. для реализации взаимодействия с какой-то своей тестируемой системой, достаточно реализовать специальный python интерфейс и положить его (подключить) в нужное место. Как написать свой интерфейс для тестирования будет рассказано во второй части. Пока же я опишу кратко возможности. Поскольку их много всяких, я расскажу об основных, про остальные можно почитать в документации wiki.etersoft.ru/UniSet2/testsuite [3]
Конечно хотелось бы, чтобы не просто исполнялись тестовые сценарии, а чтобы ещё и при запуске теста, запускались все необходимые для теста программы. Для этого случая в тестовом сценарии предусмотрена секция RunList
Псевдопример:
<?xml version = '1.0' encoding = 'utf-8'?>
<TestScenario>
<RunList after_run_pause="5000">
<item after_run_pause="2000" script="./start_my_testprog1.sh" args="arg1 arg2" name="TestProg1"/>
<item script="./start_my_testprog2.sh" args="arg1 arg2" chdir="../../TestPrograms/" name="TestProg2"/>
<item script="./start_my_testprog3.sh" args="arg1 arg2" ignore_terminated="1" name="TestProg3"/>
</RunList>
<TestList>
...тесты..
</TestList>
<TestScenario>
В этой секции можно задать список того, что нужно запустить перед началом теста. Для каждой запускаемой программы указываются параметры запуска (args), программы могут лежать в других каталогах (chdir). Можно задать уникальное имя (name), которое в случае вылета программы будет выведено в логах. При этом как видно из примера, так же можно задать паузы после запуска той или иной программы или всех вместе. Можно задать параметр silent_mode=[0,1] , где 1 означает перенаправить весь вывод в /dev/null). А можно указать параметр logfilе=«filename», при котором весь вывод будет перенаправлен в указанный файл (отменяет действие silent_mode).
Все программы запускаются в фоновом режиме. По умолчанию, если какая-либо из них вдруг во время теста вылетает, тест прерывается с ошибкой. Но это можно отключить параметром ignore_terminated=«1». Все запущенные программы завершаются после прохождения теста (не важно успешного или нет). Т.е. в итоге RunList позволяет запускать необходимое окружение для тестирования, которое будет автоматически завершено после. Ну либо производить какие-то подготовительные действия перед началом теста.
Ещё одной возможностью uniset2-testsuite является запуск указанных скриптов (программ) при успешном или провальном прохождении теста.
<?xml version="1.0" encoding="utf-8"?>
<TestScenario>
<Success>
<item script="./success.sh param1 param2"/>
<item script="./success.sh param3 param4"/>
</Success>
<Failure>
<item script="./failure.sh param1 param2"/>
<item script="./failure.sh param3 param4"/>
</Failure>
....
...
</TestScenatio>
Это можно использовать в разных случаях. Например (из очевидных):
Когда у Вас становится много развесистых тестов, которые содержат «полное покрытие», возможно Вам в некоторых случаях не хочется прогонять их все, для того, чтобы проверить какую-то одну «логическую ветку тестов». Для решения этой задачи на помощь приходят «теги». Вы можете помечать каждый тест одним или несколькими тегами
...
<test name="Test3" tags="#tag1#tag2">
<check test="outlink" file="Test4.xml" link="ALL"/>
</test>
<test name="Test5" tags="#tag1">
<check test="outlink" file="Test6.xml" link="ALL"/>
</test>
<test name="Test7" tags="#tag2">
<check test="outlink" file="Test8.xml" link="ALL"/>
</test>
...
И при запуске сценария можно указать, что нужно исполнить только тесты с указанными тегами --play-tags "#tag1#tag2#".
Т.к. существует механизм вызова тестов из других тестов и других файлов, то при достаточно сложной структуре тестов хочется видеть, как они будут вызваны, в какой последовательности и «кто кого вызывает». Для этого существует специальная команда, выводящая дерево тестов на экран --show-test-tree
Test1
Test2
Test3
Test 4
Test 5
Test 6
Test 7
При этом если для этой команды указать--play-tags "#tag1#tag2#", то будет выведено дерево тестов с учётом тегов.
Test1 [#tag1]
Test2 [#tag2]
Test3 [#tag1]
Немаловажной частью при сложной структуре тестов является проверка корректности.
Некоторые тесты могут проходить довольно длительное время. И бывает «обидно», когда через час работы теста он вываливается на какой-нибудь «опечатке» в названии. Для решения этой проблемы предусмотрено два специальных режима проверки теста:
В этих режимах фактического «исполнения» проверок не происходит, нет пауз и timeout-ов. Т.е. происходит просто «синтаксическая» проверка теста и параметров на корректность.
Вот так выглядит результат:
Если при вылете теста хочется увидеть trace вызовов, есть специальный параметр --print-calltrace, выводящий на экран дерево вызовов от места вылета к началу. При этом есть специальный параметр, ограничивающий глубину --print-calltrace-limit N
Примерно так это выглядит
Безусловно, «шаблоны» это удобная вещь. И uniset2-testsuite тоже есть простая реализация этого механизма. В данном случае, речь идёт о простом способе автоматической замены «одного» на «другое». Например, пишем:
...
<test name="Check [MyVariable]" lname="tname">
....
<check test="[MyVariable]=[MyValue]"/>
</test>
...
А потом хотим вызывать этот тест с различными параметрами в качестве MyVariable, MyValue. Легко…
...
<test name="Check Name1=100">
<check test="outlink" file="my-template-test.xml" link="lname=tname" replace="[MyVariable]:Name1,[MyValue]:100"/>
</test>
<test name="Check Name2=200">
<check test="outlink" file="my-template-test.xml" link="lname=tname" replace="[MyVariable]:Name2,[MyValue]:200"/>
</test>
...
Правила replace имеют три зоны видимости:
Глобальные replace прописываются в секции TestList
<TestList replace="[MyVariable]:Name2,[MyValue]:200"
Уровень теста прописывается в секции test и действует только на данный тест и все link, outlink которые используются внутри него.
<test name="MySuperTes replace="[MyVariable]:Name2,[MyValue]:200">
<check test="[MyVariable]=11"/>
<check test="Othrer=[MyValue]"/>
<check test="outlink" file="my-template-test.xml" link="lname=tname"/>
</test>
Уровень конкретной проверки прописывается в секции check и действует только на конкретный вызов link или outlink теста
<test name="Check Name2=200">
<check test="outlink" file="my-template-test.xml" link="lname=tname" replace="[MyVariable]:Name2,[MyValue]:200" />
</test>
Хочется заметить, что данный механизм замен (replace) действует не только на теги action, check, set, test, но так же и на названия тестов, их параметры и даже свойства replace во вложенных тестах.
Ну и под конец то, о чём не рассказал:
Usage: uniset2-testsuite-xmlplayer [--confile [configure.xml|alias@conf1.xml,alias2@conf2.xml,..] --testfile scenario.xml [..other options..]
--testfile tests.xml - Test scenarion file.
--test-name test1,prop2=test2,prop3=test3,... - Run tests from list. By default prop=name
--ignore-run-list - Ignore <RunList>
--ignore-nodes - Do not use '@node' or do not check node available for check scenario mode
--default-timeout msec - Default <check timeout='..' ../>.'
--default-check-pause msec - Default <check check_pause='..' ../>.'
--print-calltrace - Display test call trace with test file name. If test-suite FAILED.
--print-calltrace-limit N - How many recent calls to print. Default: 20.
--supplier-name name - ObjectName for testsuite under which the value is stored in the SM. Default: AdminID.
--check-scenario - Enable 'check scenario mode'. Ignore for all tests result. Only check parameters
--check-scenario-ignore-failed - Enable 'check scenario mode'. Ignore for all tests result and checks
--play-tags '#tag1#tag2#tag3..' - Play tests only with the specified tag
--show-test-tree - Show tree of tests
--hide-result-report - Hide result report
--confile [conf.xml,alias1@conf.xml,..] - Configuration file for uniset test scenario.
TestSuiteConsoleReporter (--log)
--------------------------------------------
--log-show-tests - Show tests log
--log-show-actions - Show actions log
--log-show-result-only - Show only result report (Ignore [show-actions,show-tests])
--log-show-comments - Display all comments (test,check,action)
--log-show-numline - Display line numbers
--log-show-timestamp - Display the time
--log-show-test-filename - Display test filename in test tree
--log-show-test-comment - Display test comment
--log-show-test-type - Display the test test type
--log-hide-time - Hide elasped time
--log-hide-msec - Hide milliseconds
--log-col-comment-width val - Width for column "comment"
--log-no-coloring-output - Disable colorization output
--log-calltrace-disable-extended-info - Disable show calltrace extended information
TestSuiteLogFileReporter (--logfile)
--------------------------------------------
--logfile-name filename - Save log to file
--logfile-trunc - Truncate logile
--logfile-flush - flush every write
TestSuiteJUnitReporter (--junit)
--------------------------------------------
--junit-filename name - Save junit report to file
--junit-deep val - Deep tree of test for report. '-1' - all tests
Во второй части рассмотрим как написать свой интерфейс для проверки.
Автор: PavelVainerman
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/250072
Ссылки в тексте:
[1] libuniset2: https://habrahabr.ru/post/278535/
[2] uniset2-testsuite: https://github.com/Etersoft/uniset2-testsuite
[3] wiki.etersoft.ru/UniSet2/testsuite: http://wiki.etersoft.ru/UniSet2/testsuite/
[4] Источник: https://habrahabr.ru/post/323290/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.