--- name: basex-test description: Use this skill when working with BaseX XQuery unit tests metadata: author: BaseX version: "12.2" homepage: https://docs.basex.org/main/Unit_Functions allowed-tools: Read --- # Unit Functions This BaseX module contains annotations and functions for performing XQUnit tests. A test is an XQuery function with the `%unit:test` annotation. ## [Introduction](#introduction) The more complex a software application grows, the more error-prone it gets. This is why testing frameworks exist, which provide a standardized, automated way of testing software. The [xUnit](https://en.wikipedia.org/wiki/XUnit) frameworks (such as SUnit or JUnit) allow testing of atomic units of a program, such as single functions and algorithms. This module borrows heavily from the existing frameworks: it provides various annotations for testing XQuery functions. Unit functions are provided to assert the validity of arbitrary conditions expressed in XQuery and to raise errors whenever a condition is not satisfied. Some additional functions exist to run all unit tests of the current module or a set of specified library modules. ## [Usage](#usage) Tests are started via the `[TEST](Commands#test)` command. It compiles all XQuery modules in a given file or directory and runs all functions that are annotated with `%unit:test`. A test report is generated and returned, which resembles the format returned by other xUnit testing frameworks, such as the Maven Surefire Plugin ([see below](#result)). ## [Conventions](#conventions) All annotations, functions and errors in this module are assigned to the `http://basex.org/modules/unit` namespace, which is statically bound to the `unit` prefix. ## [Annotations](#annotations) ### [`unit:test`](#unit:test) | | | | --- | --- | | Syntax | `unit:test()`
`unit:test("expected", CODE)` | | Summary | With this annotation, a function can be marked as unit test. It will be evaluated if a test report is created for the module in which this function is located. `error` can be supplied as additional string argument. It is followed by `CODE`, which must be a valid [EQName](XQuery_3.0#expanded_qnames) string. If the function expression does not raise that error, the test will fail. | | Examples | ```xquery
declare %unit:test function local:void() { () };
```

The following test will be successful, as it does nothing (and, hence, nothing wrong).

- - -

```xquery
declare %unit:test('expected', "err:XPTY0004") function local:add() {
123 + 'strings and integers cannot be added'
};
```

The following test will be successful, as the function body will raise `err:XPTY0004`. | ### [`unit:before`](#unit:before) | | | | --- | --- | | Syntax | `unit:before()`
`unit:before(FUNCTION)` | | Summary | A function decorated with this annotation will be evaluated **before each** unit test as a separate transaction. `FUNCTION` can be supplied as additional argument. It must be a valid [EQName](XQuery_3.0#expanded_qnames) string. If specified, the function will only be evaluated before a function with the given name is tested. This extension is e. g. helpful if the results of updates need to be tested. | | Examples | ```xquery
declare %updating %unit:before("local:check") function local:before-check() {
db:create('test-db')
};
declare %updating %unit:test function local:check() {
unit:assert(db:exists('test-db'))
};
```

The first function will be evaluated before the actual test. | ### [`unit:after`](#unit:after) | | | | --- | --- | | Syntax | `unit:after()`
`unit:after(FUNCTION)` | | Summary | A function decorated with this annotation will be evaluated **after each** unit test as a separate transaction. `FUNCTION` can be supplied as additional argument. It must be a valid [EQName](XQuery_3.0#expanded_qnames) string. If specified, the function will only be evaluated after a function with the given name is tested. | ### [`unit:before-module`](#unit:before-module) | | | | --- | --- | | Syntax | `unit:before-module()` | | Summary | If a function is decorated with this annotation, it will be evaluated **before all** unit tests in the current module as a separate transaction. | ### [`unit:after-module`](#unit:after-module) | | | | --- | --- | | Syntax | `unit:after-module()` | | Summary | If a function is decorated with this annotation, it will be evaluated **after all** unit tests in the current module as a separate transaction. | ### [`unit:ignore`](#unit:ignore) | | | | --- | --- | | Syntax | `unit:ignore()`
`unit:ignore(MESSAGE)` | | Summary | If a function is decorated with this annotation, it will temporarily be ignored by the test suite runner. | ## [Functions](#functions) ### [`unit:assert`](#unit:assert) Signature unit:assert( $test as item()\*, $info as item() := () ) as empty-sequence() Summary Asserts that the effective boolean value of the specified `$test` is true and returns an empty sequence. Otherwise, raises an error. The _effective boolean value_ of an expression can be explicitly computed by using the `fn:boolean` function. The default failure message can be overridden with the `$info` argument.Errors | | | | --- | --- | | [`fail`](#errors) | An assertion failed, or an error was raised. | ### [`unit:assert-equals`](#unit:assert-equals) Signature unit:assert-equals( $returned as item()\*, $expected as item()\*, $info as item() := () ) as empty-sequence() Summary Asserts that the specified arguments are equal according to the rules of the `[fn:deep-equal](Standard_Functions#fn:deep-equal)` function. Otherwise, raises an error. The default failure message can be overridden with the `$info` argument.Errors | | | | --- | --- | | [`fail`](#errors) | An assertion failed, or an error was raised. | ### [`unit:fail`](#unit:fail) Signature unit:fail( $info as item() := () ) as empty-sequence() Summary Raises a unit error. The default failure message can be overridden with the `$info` argument.Errors | | | | --- | --- | | [`fail`](#errors) | An assertion failed, or an error was raised. | ## [Example](#example) The following XQUnit module `tests.xqm` contains all available unit annotations: ### [Query](#query) ```xquery module namespace test = 'http://basex.org/modules/xqunit-tests'; (:~ Initializing function, which is called once before all tests. :) declare %unit:before-module function test:before-all-tests() { () }; (:~ Initializing function, which is called once after all tests. :) declare %unit:after-module function test:after-all-tests() { () }; (:~ Initializing function, which is called before each test. :) declare %unit:before function test:before() { () }; (:~ Initializing function, which is called after each test. :) declare %unit:after function test:after() { () }; (:~ Function demonstrating a successful test. :) declare %unit:test function test:assert-success() { unit:assert() }; (:~ Function demonstrating a failure using unit:assert. :) declare %unit:test function test:assert-failure() { unit:assert((), 'Empty sequence.') }; (:~ Function demonstrating a failure using unit:assert-equals. :) declare %unit:test function test:assert-equals-failure() { unit:assert-equals(4 + 5, 6) }; (:~ Function demonstrating an unexpected success. :) declare %unit:test("expected", "err:FORG0001") function test:unexpected-success() { () }; (:~ Function demonstrating an expected failure. :) declare %unit:test("expected", "err:FORG0001") function test:expected-failure() { 1 + }; (:~ Function demonstrating the creation of a failure. :) declare %unit:test function test:failure() { unit:fail("Failure!") }; (:~ Function demonstrating an error. :) declare %unit:test function test:error() { 1 + }; (:~ Skipping a test. :) declare %unit:test %unit:ignore("Skipped!") function test:skipped() { () }; ``` By running `TEST tests.xqm`, the following report will be generated (timings may differ): ### [Result](#result) ```xml Empty sequence. 9 6 Item 1: 6 expected, 9 returned. FORG0001 Failure! Cannot cast to xs:double: "". ``` ## [Errors](#errors) | Code | Description | | --- | --- | | `fail` | An assertion failed, or an error was raised. | | `no-args` | A test function must have no arguments. | | `private` | A test function must not be private. | ## [Changelog](#changelog) **Version 9.0** * Updated: error codes updated; errors now use the module namespace **Version 8.0.2** * Updated: (expected) errors are compared by QNames instead of local names (including namespaces). **Version 8.0** * Added: `[unit:fail](#unit:fail)`, 0-argument signature. * Updated: the info argument of functions can now be an arbitrary item. * Updated: infos are now represented in an `info` child element. * Updated: `[unit:before](#unit:before)` and `[unit:after](#unit:after)` can be extended by a filter argument. * Deleted: `UNIT0006` (ignore results returned by functions). **Version 7.9** * Added: TEST command * Removed: `[unit:test](#unit:test)`, `unit:test-uris` **Version 7.8** * Added: `[unit:assert-equals](#unit:assert-equals)` * Updated: enhanced test report output **Version 7.7** * Added: New module added.