274 lines
No EOL
10 KiB
Markdown
274 lines
No EOL
10 KiB
Markdown
---
|
||
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. Each 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()` <br>`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<br>declare %unit:test function local:void() { () };<br>```<br><br>The following test will be successful, as it does nothing (and, hence, nothing wrong).<br><br>- - -<br><br>```xquery<br>declare %unit:test('expected', "err:XPTY0004") function local:add() {<br> 123 + 'strings and integers cannot be added'<br>};<br>```<br><br>The following test will be successful, as the function body will raise `err:XPTY0004`. |
|
||
|
||
### [`unit:before`](#unit:before)
|
||
|
||
| | |
|
||
| --- | --- |
|
||
| Syntax | `unit:before()` <br>`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<br>declare %updating %unit:before("local:check") function local:before-check() {<br> db:create('test-db')<br>};<br>declare %updating %unit:test function local:check() {<br> unit:assert(db:exists('test-db'))<br>};<br>```<br><br>The first function will be evaluated before the actual test. |
|
||
|
||
### [`unit:after`](#unit:after)
|
||
|
||
| | |
|
||
| --- | --- |
|
||
| Syntax | `unit:after()` <br>`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()` <br>`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()
|
||
|
||
SummaryAsserts 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()
|
||
|
||
SummaryAsserts 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()
|
||
|
||
SummaryRaises 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(<a/>)
|
||
};
|
||
|
||
(:~ 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 + <a/>
|
||
};
|
||
|
||
(:~ 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 + <a/>
|
||
};
|
||
|
||
(:~ 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
|
||
<testsuites time="PT0.256S">
|
||
<testsuite name="file:///C:/Users/user/Desktop/test.xqm"
|
||
time="PT0.212S" tests="8" failures="4" errors="1" skipped="1">
|
||
<testcase name="assert-success" time="PT0.016S"/>
|
||
<testcase name="assert-failure" time="PT0.005S">
|
||
<failure line="30" column="15">
|
||
<info>Empty sequence.</info>
|
||
</failure>
|
||
</testcase>
|
||
<testcase name="assert-equals-failure" time="PT0.006S">
|
||
<failure line="35" column="22">
|
||
<returned item="1" type="xs:integer">9</returned>
|
||
<expected item="1" type="xs:integer">6</expected>
|
||
<info>Item 1: 6 expected, 9 returned.</info>
|
||
</failure>
|
||
</testcase>
|
||
<testcase name="unexpected-success" time="PT0.006S">
|
||
<failure>
|
||
<expected>FORG0001</expected>
|
||
</failure>
|
||
</testcase>
|
||
<testcase name="expected-failure" time="PT0.004S"/>
|
||
<testcase name="failure" time="PT0.004S">
|
||
<failure line="50" column="13">
|
||
<info>Failure!</info>
|
||
</failure>
|
||
</testcase>
|
||
<testcase name="error" time="PT0.004S">
|
||
<error line="55" column="6" type="FORG0001">
|
||
<info>Cannot cast to xs:double: "".</info>
|
||
</error>
|
||
</testcase>
|
||
<testcase name="skipped" skipped="Skipped!" time="PT0S"/>
|
||
</testsuite>
|
||
</testsuites>
|
||
```
|
||
|
||
## [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. |