|
2008-11-03
Скачать дистрибутив PGUnit для PostgreSQL 8.3:
dklab_pgunit_2008-11-09.sql
Что такое PGUnit?
PGUnit это фреймворк для написания unit-тестов в стиле xUnit для PostgreSQL 8.3+.
Он позволяет разработчикам хранимых процедур писать автоматические unit-тесты для
существующих процедур, а также создавать новые процедуры согласно концепции Test Driven
Development (TDD). Код тестов записывается в саму базу данных, поэтому для хранения и
запуска тестов нет необходимости использовать внешние ресурсы (такие как файлы, системы
контроля версий, утилиты командной строки и т. д.).
Как и в традиционном xUnit, набор тестов группируется в "тест-кейсы"; каждый кейс может
иметь свой собственный код инициализации окружения, общего для всех тестов группы ("код подготовки фикстуры" или "блок setUp").
Главное преимущество PGUnit в том, что блок setUp (часто довольно тяжеловесный) выполняется
только один раз в рамках тест-кейса, а эффект от его выполнения сохраняется
в savepoint базы данных. В дальнейшем все тесты исполняются начиная с этой savepoint,
так что накладные расходы на инициализацию фикстуры минимальны. (Иными словами, фикстура
инициализируется только однажды, но результат этой инициализации сохраняется и
применяется многократно.) При этом все тесты по прежнему запускаются независимо друг от
друга, потому что эффект от запуска теста автоматически "откатывается" после его
исполнения.
Как установить
Это просто: скачайте и запустите как обычный SQL-скрипт. В вашей базе данных
появится новая схема с именем "pgunit"; она содержит все необходимые функции.
|
А как же pgTAP? Зачем еще один тестовый фреймворк?
Дело в том, что pgTAP для нас оказался недостаточно прозрачным. Но, конечно, возможно,
что вам следует использовать pgTAP, а не PGUnit - изучите его тоже.
|
Как пользоваться
Собственно, PGUnit работает точно так же, как и любые xUnit-фреймворки (например, PHPUnit).
Так что, научившись работать с тем же PHPUnit, вы легко поймете и принципы PGUnit.
Типичный процесс использования PGUnit заключается в следующем.
- Пишется процедура с тестовым кодом и именем test_* для вашей базы данных (см. примеры ниже).
- Запускается команда:
SELECT pgunit.testrunner(NULL);
Эта команда сканирует
БД на наличие в ней процедур с именем test_* (и соответствующим типом
возвращаемого значения), а затем - последовательно запускает их на выполнения,
каждую - в изолированной транзакции.
- Можно также выполнить не все, а только некоторые тесты:
SELECT pgunit.testrunner('first%testname second%testname ...')
Разрешено использовать подстановочные знаки оператора LIKE, а также
перечислять несколько имен тестов через пробел.
- Результаты выполнения тестового кода "откатываются" и не оставляют в БД следа.
Пример тестового кода
Все достаточно просто. Вам нужно создать специальную функцию на языке SQL и
определить в ней тестовые кусочки кода. Как и в традиционных unit-тестах, имеется
секция setUp, выполняющаяся перед каждым тестом и запускающая общую для всех тестов
инициализацию, и набор именованных кусков тестового кода, которые работают
изолированно друг от друга.
|
Важно понимать, что на логическом уровне код в секции setUp выполняется столько раз,
сколько существует кусков тестового кода. Т.е. эффект от запуска setUp как бы
"приклеивается" к коду теста и выполняется в единой с ним транзакции.
|
CREATE FUNCTION pgunit.test_sample () RETURNS testfunc[]
AS
$body$
SELECT pgunit.testcase(
$setUp$
-- setUp code is executed before ANY test function code (see below).
-- Effect of this execution is persistent only during the code
-- block execution and rolled back after the test is finished.
CREATE TABLE tst(id INTEGER);
$setUp$,
ARRAY[
-- This is a first test function code. Just check if we can insert
-- into the table created in setUp.
'first test: insert is okay', $sql$
INSERT INTO tst VALUES(1);
PERFORM pgunit.assert_same(1, (SELECT * FROM tst));
$sql$,
-- This is a second test function code.
-- Illustrates that the effect of the first function is not visible.
'second test: effect of previous function is not visible here', $sql$
PERFORM pgunit.assert_same(NULL, (SELECT * FROM tst));
$sql$,
-- This is a third test function code. Illustrate that we may use DECLARE.
'first test: you may use DECLARE in tests', $sql$
DECLARE
i INTEGER;
BEGIN
FOR i IN 1 .. 10 LOOP
INSERT INTO tst VALUES(i);
PERFORM pgunit.assert_same(i, (SELECT * FROM tst WHERE id = i));
END LOOP;
END;
$sql$
]
);
$body$
LANGUAGE sql;
Пример результата работы
А вот пример результата работы тестов, запущенных командой SELECT pgunit.testrunner(NULL).
Я специально "поломал" один тест, чтобы показать, как это выглядит. Формат вывода совместим
с PHPUnit.
NOTICE: PGUnit 2008-11-03 by Dmitry Koterov.
NOTICE:
NOTICE: pgunit.test_assert_same
NOTICE: - OK matchedValues
NOTICE: - OK mismatchedValues
NOTICE: pgunit.test_exec
NOTICE: ! FAIL executed
NOTICE: pgunit.test_runtest
NOTICE: - OK executed
NOTICE: - OK exception hook
NOTICE: pgunit.test_sample
NOTICE: - OK first test: insert is okay
NOTICE: - OK second test: effect of previous function is not visible here
NOTICE: pgunit.test_wildcard_match
NOTICE: - OK one value
NOTICE: - OK two values
NOTICE: - OK no matches
NOTICE:
NOTICE: Time: 00:00:00
NOTICE:
NOTICE: There was 1 failure
NOTICE:
NOTICE: 1) executed(pgunit.test_exec)
NOTICE: Failed asserting that two things are same.
NOTICE: expected string <2>
NOTICE: got string <1>
NOTICE:
NOTICE: FAILURES!
NOTICE: Tests: 10, Failures: 1.
Changelog
2008-11-09
- A little code simplification (undocumented "WHEN others THEN ..."
instead of exception class enumeration).
2008-11-07
- Great speed-up while running a bunch of test with a single setUp block.
In this case setUp is executed ONLY ONCE, and the effect of its
execution is stored in a savepoint which is used for all
individually-rollbacked tests. (All tests are still running
independently and are not intersected with each other.)
- Time measurnment of each test execution (thanks to Garrynja).
- Supress NOTICEs generated by a setUp or test code to keep the
execution log clean.
2008-11-03
- First release.
|
|
Пользуетесь? Нравится?.. Пожертвуйте!
Пожертвование пойдет в качестве "спасибо" прямиком
на поддержание мотивации автора. А то он даже не знает, какое
количество людей пользуется той или иной библиотекой
или утилитой...
|