Introduction
Caius is a functional testing framework written in object-oriented Tcl using the [incr Tcl] extension. It provides intuitive, object-oriented programming interfaces to Expect and Selenium WebDriver.
The caius
command line tool provides a generic test runner which allows you
to execute tests written in any programming language and test framework. The
integrated report generator collects test results and turns them into legible,
clean test reports.
Installation
In order to install and try out Caius on your system, follow the instructions in this section.
Pre-requisites
Caius is developed and tested on Debian/GNU Linux but should run on other Linux distributions without problems. Using Caius on BSD or other Unix-like systems may require minor modifications.
Caius requires Tcl 8.6 or later with thread support, a recent version of tcllib and Expect. In addition, the tdom package is required for results parsing and report generation.
Installation from Source
Caius comes with a simple installer script that will copy modules and scripts into your Tcl package path. After unpacking the sources, execute the installer script via
sudo ./install.tcl
After that you should be able to call caius
from the command line.
Writing Tests
This section explains how to write test modules and how to execute them.
Outline of a Test Class
In Caius, test modules are classes which derive from Testing::TestObject
. A
minimal test module looks like this:
package require Itcl
package require Testing
itcl::class MyTests {
inherit Testing::TestObject
method test_passing {} {
puts "Hello, I pass!"
}
method test_failing {} {
error "Hello, I'm about to fail!"
}
method test_fail_with_assertion {} {
Testing::assert {1 == 2}
}
}
exit [[MyTests #auto] run $::argv]
The test class MyTests
inherits a lot of functionality from
Testing::TestObject
, most importantly the run
method. The run
method is
responsible for parsing any command line arguments and initiating the test
execution.
A test is any method that starts with the prefix test
. Tests are executed
in lexicographical order. If you absolutely need your tests to run in a certain
order, you can use prefixes like test_01
, test_02
, etc.
Test methods don't take any parameters and their return value is ignored. To
make a test fail, you have to raise an error. This can be a normal Tcl error or
an Exception thrown using the raise
command from Caius' Error
package.
Repeated Setup and Teardown
If you need to do repeated setup and cleanup work before and after each test
case, you can define methods setup
and teardown
. They will be invoked
automatically for you.
One-time Preparation and Cleanup
If you need work to be done once on entry to your test module and once again
after the last test case has been run, don't use the constructor and
destructor of your test class for this purpose. Instead define methods
setup_before
and teardown_after
.
Documenting Your Tests
In order to document your tests, you may define docstrings for your test methods as shown in the following example:
itcl::class MyTest {
inherit Testing::TestObject
method test_something {} {
docstr "This is an example docstring.
Test steps:
* step 1
* step 2
Docstrings are written in
\[markdown syntax](http://daringfireball.net/projects/markdown/syntax).
"
# implementation here
}
}
Docstrings are a concept stolen from Python. They are a special feature
of Caius and not generally available in Tcl. They only work in test methods.
The docstr
command must be the very first statement in the method body.
Docstrings are written in markdown syntax. They are included in the test results output and converted to HTML for display in test reports. Note that since docstrings are Tcl strings, braces, brackets and quotation marks need to be escaped.
Making the Test Script Executable
In order to make a test script executable, simply add a shebang line at the top of the script:
#!/usr/bin/tclsh
Then mark the script as executable:
chmod +x test.tcl
If your distribution ships with multiple versions of Tcl, or if Tcl 8.6 is not
the default version, you may have to be explicit about which tclsh
you would
like to be invoked.
Running Tests
This section explains how to execute your tests individually or as part of a test plan.
Direct Execution of Test Scripts
Test scripts that invoke the run
method on a test object are fully
self-contained and can be called directly. In order to see which parameters
you can pass to your script, call it with the --help
command line switch:
./test.tcl --help
In order to run all the tests, invoke the script without any arguments. Or if you would like to run only selected tests, specify them separately on the command line:
./test.tcl test1 test2 ...
In order to see which tests are available in your module, use the --list
command line switch:
./test.tcl -l
Per default, test scripts print the test results in an XML-based reporting
format. These XML reports have a simple structure, but if you need something
more readable, you can instruct the script to output logs and results in a
nicely indented text format using the --format
parameter:
./test.tcl -f text
Using the Caius Testrunner Application
When executing your tests in an automated fashion, for example from a CI
system, it is recommended that you invoke them indirectly using the
caius run
command. Among other things, caius run
can let your test scripts
timeout, which is essential if you want to avoid getting stuck on badly
written or misbehaving test cases.
In order to execute a test script with a 60 seconds timeout invoke it like this:
caius run -t 60 path/to/test
The caius run
command will also do the right thing and recover in case your
script dies prematurely and/or for some reason produces an incomplete or
invalid results XML. Whenever the output of a script is valid XML, the test
runner will save it as it is. If a script produces broken XML or any other
format, the test runner will automatically convert it into a valid XML report.
An important implication of this is that caius run
can run tests written in
any language and any framework. The only requirement is that tests terminate
with a non-zero exit status on failure.
Creating and Executing a Test Plan
Test plans are written in a simple XML format and define a sequence of test scripts to be executed. The following listing shows a basic example:
<testplan>
<run timeout="60">/home/tobias/Demo/test1.tcl</run>
<run timeout="60">/home/tobias/Demo/test2.tcl</run>
...
</testplan>
Test plans are loaded and executed with the caius runplan
command. For each
test, a subdirectory will be created inside the current working directory. This
subdirectory will be made the working directory of the respective test script
itself.
You can explicitly set the initial working directory of the runplan
command
using the --work-dir
command line switch.
Working with Constraints
Let's say you are testing a range of network devices offering connectivity through an arbitrary combination of Ethernet, Wifi, DSL and UMTS. You have tests for each feature and want to enable them selectively according to the capabilities of the device under test.
To this end, you may attach constraints to arbitrary blocks of code or entire test methods:
# test works only for devices that have a wifi chip and a DSL modem
::Testing::constraints {wifi dsl} {
method test_share_dsl_connection_over_wifi {} {
# test code here ...
}
}
When executing your test module, you can use the --constraint
command line
parameter (also multiple times) to declare constraints to be true for the
duration of the test run:
./mytest.tcl -c wifi -c dsl
Alternatively, you can export a list of constraints to the environment
variable CAIUS_TEST_CONSTRAINTS
.
Executing Tests Matching a Pattern
You may choose to execute only certain tests from a test module by matching
test names against a wildcard pattern with the --match
command line parameter.
For example, in order to run all tests that have the string connectivity in
their name you could say:
./mytest.tcl -m "*connectivity*"
Be careful to quote patterns to prevent your shell from interpreting them. The
--match
option or its -m
short-hand may be used multiple times.
Generating Reports
In order to generate a report from the results of a testrun, use the
caius report
command. It takes as a single argument the directory which to
scan for results.
The directory should contain subdirectories created by a previous call to
caius runplan
. Each of these in turn should contain exactly one results XML
file named result.xml plus any number of artifacts.
The test report will be created inside the given directory as one or more HTML files.
Integration with Jenkins
In Jenkins, create a new project and configure your build as you normally would. Then open the project settings, go to the Build section and add an extra build step for running the tests. The following snippet might serve as an example:
cd test
rm -fr spool/*
caius runplan -d spool -f junit testplan.xml
Note that we pass -f junit
to caius runplan
indicating that test results
should be emitted in JUnit XML result format. Next, go to section Post-build
Actions and choose Publish JUnit test result report. As the fileset specify
test/spool/*/result.xml
Finally double check that Jenkins has all the necessary permissions and access to relevant infrastructure required for executing your tests successfully. Trigger a build and enjoy your test results being reported natively in Jenkins.