Unit testing with LibPQ
LibPQ offers a basic unit testing framework, which consists of the following modules:
- UnitTest.Assert - a collection of assertion functions
- UnitTest.Constants - framework-wide constants
- UnitTest.Discover - discover and run unit tests from all local sources
- UnitTest.Run - runner for individual test suites
- UnitTest.Subtests - helper function for writing subtests
Test function (test case)
Individual test cases are implemented using test functions. A test function is a function that takes zero arguments and produces one of three outcomes when called:
- If test is succesfull (PASSED) the test function returns any value. The value itself is irrelevant.
- If test is unsuccesfull (FAILED) the test function raises
LibPQ.AssertionError. (This error reason is defined in UnitTest.Constants)
- If the test code itself raises an error the test is considered neither succesfull nor failed, and the error must not be silenced (ERROR)
A test suite is a collection of test functions, usually related to each other in some way. Test suite has to store test functions as a record with field names starting with "test" (the test prefix is defined in UnitTest.Constants).
Any field of the test suite which name does not start with "test" prefix is ignored by the UnitTest framework.
It is recommended to add a metadata field to the test suite record to enable
automated tests discovery. The field and its value are
LibPQ.TestSuite = 1.
1 indicates the version of test suite format and will be
incremented in future if any non-backwards compatible changes will be
Suites without the metadata will still be executable by test runners but will not be visible to discovery tools.
In order to simplify writing the tests LibPQ offers some useful assertion functions in UnitTest.Assert module.
Each of the functions in that module is a valid test function that tests some simple assertion. You can use them as building blocks for writing your own tests.
When you need to run the same tests against slightly different parameters writing all the variations one by one can become tedious.
LibPQ will treat a list of test functions that are stored in the same test suite field as a group of subtests. It will execute them until an error occurs, and that error will be treated as the test result.
There is also a helper function that simplifies creating subtests.
At this moment there are no special tools for defining and invoking test
tearDown field names in test suite record should be
considered reserved for that purpose.
To create test fixture now you can add a helper function to the test suite and store it in any field which name does not start with "test" prefix. Such function(s) will have to be invoked explicitly from each individual test.
Running tests and test discovery
Test runner is a function that runs a single test suite (provided as the only argument) and returns test results as a table. UnitTest.Run is a reference implementation of a test runner, use it to run individual test suites.
UnitTest.Discover is a function that automatically discovers and runs all test suites from local sources. Test discovery details:
- Test suites have to be stored in separate LibPQ modules that contain
metadata referring to the test suite version (value of
- Each test is executed with corresponding test runner, test result tables are combined into a single output table (see the reference test runner for the table structure of test results).
- Test runners are assigned to test suite versions in the UnitTest.Constants module.
Fact based unit tests
If you are familiar with fact based unit tests (as shown in Microsoft documentation) you can continue to use that approach with LibPQ UnitTest framework. Integrating existing fact based test suites will require some extra code, but the tests themselves need no modification. See this article for more information.