Oninit® ifxTAP — Getting Started
Status: pre-release.
Informix applications keep a lot of logic in the database: constraints, triggers and SPL routines. That logic rarely has tests, because testing it usually means a hand-built test database and a lot of cleanup. ifxTAP removes the cleanup. You write tests as plain SQL files. Every file runs inside a transaction that the runner rolls back, so the database is unchanged afterwards.
What you need
- Informix 14.10 or later, with a logged database. Unlogged databases are not supported, and the runner refuses them.
- Java 11 or later on the machine running the tests.
- The IBM Informix JDBC driver, supplied separately.
1. Install the library
ifxTAP is a set of SPL routines named tap_* that you install into your test database. Point the runner
at the database and install them:
./build.sh # once: builds the runner (see Install)
export PATH="$PWD/bin:$PATH"
export IFXTAP_JDBC=/path/to/jdbc.jar
export IFXTAP_URL='jdbc:informix-sqli://dbhost:9088/mydb:INFORMIXSERVER=myserver'
export IFXTAP_USER=... IFXTAP_PASSWORD=...
ifxtap --install sql/install.sql
ifxtap --check
The five export lines are set once; every ifxtap command after them, here and in CI, uses them.
The install runs in one transaction. If a routine with the same name and signature as one ifxTAP creates already exists, whether ifxTAP itself or one of yours, everything is rolled back and the runner says what clashed. A routine of yours with the same name but different parameters does not block the install: it is left alone and the runner warns.
The library can be upgraded in place with ifxtap --upgrade sql. See
Install for the details.
2. Write a test
A test file declares how many assertions it expects, does its work, and ends with tap_finish(). This
one tests a stored function:
EXECUTE PROCEDURE tap_plan(2);
CREATE FUNCTION add_interest(p_balance DECIMAL(10,2), p_pct INTEGER) RETURNING DECIMAL(10,2)
RETURN p_balance + (p_balance * p_pct / 100);
END FUNCTION;
CREATE TABLE result (v DECIMAL(10,2));
INSERT INTO result SELECT add_interest(200, 10) FROM sysmaster:sysdual;
EXECUTE PROCEDURE tap_has_function('add_interest');
EXECUTE PROCEDURE tap_is((SELECT v FROM result), 220.00, '10% on 200 is 220');
EXECUTE PROCEDURE tap_finish();
The function and the table exist only for this run. Informix DDL is transactional in a logged database, so the rollback removes them.
3. Run it
ifxtap tests/
1..2
ok 1 - function add_interest should exist
ok 2 - 10% on 200 is 220
This is TAP version 13. The exit status is non-zero if an assertion fails, the count differs from the plan, or the run does not complete. See Runner and TAP Output.
4. What you can assert
- Values:
tap_ok,tap_is,tap_isnt,tap_is_nulland pattern matches.tap_isfails if either side is NULL, so usetap_is_nullto test for NULL. - Schema:
tap_has_table,tap_has_function,tap_function_returns, and checks for columns, types, defaults, indexes, triggers and constraints. - Errors:
tap_throws_okexpects a given SQLCODE andtap_lives_okexpects a statement to succeed.
EXECUTE PROCEDURE tap_throws_ok('INSERT INTO product VALUES (''A-100'', 5)', -268, 'a duplicate SKU is rejected');
The full list is in the Assertion Reference.
5. Share a fixture
Statements at the top of a file run once, as a fixture for the whole file. To give each test a clean start, divide the file into sections with marker comments:
EXECUTE PROCEDURE tap_plan(2);
CREATE TABLE account (acct_id INTEGER, balance DECIMAL(10,2));
-- @setup
INSERT INTO account VALUES (1, 100);
-- @test a deposit raises the balance
UPDATE account SET balance = balance + 50 WHERE acct_id = 1;
EXECUTE PROCEDURE tap_is((SELECT balance FROM account WHERE acct_id = 1), 150.00, 'balance is 150');
-- @test the next test starts from the fixture again
EXECUTE PROCEDURE tap_is((SELECT balance FROM account WHERE acct_id = 1), 100.00, 'balance is back to 100');
EXECUTE PROCEDURE tap_finish();
Each -- @test runs in its own savepoint, so one test's changes never reach the next. There is also a
-- @teardown section. More complete tests are on the Examples page.
6. Put it in CI
Run ifxtap as a build step and let the exit status decide:
ifxtap tests/ # with IFXTAP_JDBC, IFXTAP_URL, IFXTAP_USER and IFXTAP_PASSWORD set in the job's environment
A failed assertion fails the build. The exit status is the signal; the TAP output of a run of several files is one stream
with a single plan, so a TAP tool such as prove reads it as one run. See
Runner and TAP Output.
What to keep in mind
- Leave transactions to the runner. The runner refuses
COMMIT,ROLLBACKandBEGIN WORKin a test file (it does not run them), because a commit would keep your fixtures. - Some effects are not rolled back: sequence values, writes to RAW tables, routines that commit (the runner fails the file when one does, but cannot undo it), and anything outside the database such as
SYSTEMcalls. Use a-- @teardownsection for those. How It Works lists them. - Failures explain themselves. A failed assertion reports the expected and actual values as TAP diagnostics, so a CI log shows what differed.