It's easy to create custom providers using the API from Test.Tasty.Providers.
Ingredients
Ingredients represent different actions that you can perform on your test suite.
One obvious ingredient that you want to include is one that runs tests and
reports the progress and results.
Another standard ingredient is one that simply prints the names of all tests.
It is possible to write custom ingredients using the API from Test.Tasty.Runners.
Some ingredients that can enhance your test suite are:
tasty-ant-xml adds a
possibility to write the test results in a machine-readable XML format, which
is understood by various CI systems and IDEs
tasty-rerun adds support for
minimal test reruns by recording previous test runs and using this information
to filter the test tree. For example, you can use this ingredient to only run
failed tests, or only run tests that threw an exception.
tasty-html adds the
possibility to write the test results as a HTML file
tasty-stats adds the
possibility to collect statistics of the test suite in a CSV file.
Other packages
tasty-th automatically
discovers tests based on the function names and generate the boilerplate code for
you
tasty-hunit-adapter
converts existing HUnit test suites into tasty test suites
tasty-expected-failure provides
test markers for when you expect failures or wish to ignore tests.
tasty-bench covers performance
regression testing and extends tasty to a benchmark framework
similar to criterion and gauge.
Options
Options allow one to customize the run-time behavior of the test suite, such
as:
mode of operation (run tests, list tests, run tests quietly etc.)
which tests are run (see «Patterns» below)
parameters of individual providers (like depth of search for SmallCheck)
Setting options
There are two main ways to set options:
Runtime
When using the standard console runner, the options can be passed on the
command line or via environment variables. To see the available options, run
your test suite with the --help flag. The output will look something like this
(depending on which ingredients and providers the test suite uses):
% ./test --help
Mmm... tasty test suite
Usage: test [-p|--pattern PATTERN] [-t|--timeout DURATION] [-l|--list-tests]
[-j|--num-threads NUMBER] [-q|--quiet] [--hide-successes]
[--color never|always|auto] [--ansi-tricks ARG]
[--smallcheck-depth NUMBER] [--smallcheck-max-count NUMBER]
[--quickcheck-tests NUMBER] [--quickcheck-replay SEED]
[--quickcheck-show-replay] [--quickcheck-max-size NUMBER]
[--quickcheck-max-ratio NUMBER] [--quickcheck-verbose]
[--quickcheck-shrinks NUMBER]
Available options:
-h,--help Show this help text
-p,--pattern PATTERN Select only tests which satisfy a pattern or awk
expression
-t,--timeout DURATION Timeout for individual tests (suffixes: ms,s,m,h;
default: s)
-l,--list-tests Do not run the tests; just print their names
-j,--num-threads NUMBER Number of threads to use for tests execution
(default: # of cores/capabilities)
-q,--quiet Do not produce any output; indicate success only by
the exit code
--hide-successes Do not print tests that passed successfully
--color never|always|auto
When to use colored output (default: auto)
--ansi-tricks ARG Enable various ANSI terminal tricks. Can be set to
'true' or 'false'. (default: true)
--smallcheck-depth NUMBER
Depth to use for smallcheck tests
--smallcheck-max-count NUMBER
Maximum smallcheck test count
--quickcheck-tests NUMBER
Number of test cases for QuickCheck to generate.
Underscores accepted: e.g. 10_000_000
--quickcheck-replay SEED Random seed to use for replaying a previous test run
(use same --quickcheck-max-size)
--quickcheck-show-replay Show a replay token for replaying tests
--quickcheck-max-size NUMBER
Size of the biggest test cases quickcheck generates
--quickcheck-max-ratio NUMBER
Maximum number of discared tests per successful test
before giving up
--quickcheck-verbose Show the generated test cases
--quickcheck-shrinks NUMBER
Number of shrinks allowed before QuickCheck will fail
a test
Every option can be passed via environment. To obtain the environment variable
name from the option name, replace hyphens - with underscores _, capitalize
all letters, and prepend TASTY_. For example, the environment equivalent of
--smallcheck-depth is TASTY_SMALLCHECK_DEPTH.
Note on boolean options: by convention, boolean ("on/off") options are specified
using a switch on the command line, for example --quickcheck-show-replay
instead of --quickcheck-show-replay=true. However, when
passed via the environment, the option value needs to be True or False
(case-insensitive), e.g. TASTY_QUICKCHECK_SHOW_REPLAY=true.
If you're using a non-console runner, please refer to its documentation to find
out how to configure options during the run time.
Compile-time
You can also specify options in the test suite itself, using
localOption. It can be applied not only to the whole test tree, but also to
individual tests or subgroups, so that different tests can be run with
different options.
It is possible to combine run-time and compile-time options, too, by using
adjustOption. For example, make the overall testing depth configurable
during the run time, but increase or decrease it slightly for individual
tests.
This method currently doesn't work for ingredient options, such as --quiet or
--num-threads. You can set them by setting the corresponding environment
variable before calling defaultMain:
It is possible to restrict the set of executed tests using the -p/--pattern
option.
Tasty patterns are very powerful, but if you just want to quickly run tests containing foo
somewhere in their name or in the name of an enclosing test group, you can just
pass -p foo. If you need more power, or if that didn't work as expected, read
on.
A pattern is an awk expression. When the expression is evaluated, the field $1
is set to the outermost test group name, $2 is set to the next test group
name, and so on up to $NF, which is set to the test's own name. The field $0
is set to all other fields concatenated using . as a separator.
As an example, consider a test inside two test groups:
$0 !~ /skip/ or ! /skip/ — select tests whose full names (including group names) do not contain the word skip
$NF !~ /skip/ — select tests whose own names (but not group names) do not contain the word skip
$(NF-1) ~ /QuickCheck/ — select tests whose immediate parent group name
contains QuickCheck
As an extension to the awk expression language, if a pattern pat contains only
letters, digits, and characters from the set ._ - (period, underscore, space, hyphen),
it is treated like /pat/ (and therefore matched against $0).
This is so that we can use -p foo as a shortcut for -p /foo/.
The only deviation from awk that you will likely notice is that Tasty
does not implement regular expression matching.
Instead, $1 ~ /foo/ means that the string foo occurs somewhere in $1,
case-sensitively. We want to avoid a heavy dependency of regex-tdfa or
similar libraries; however, if there is demand, regular expression support could
be added under a cabal flag.
The following operators are supported (in the order of decreasing precedence):
Syntax
Name
Type of Result
Associativity
(expr)
Grouping
Type of expr
N/A
$expr
Field reference
String
N/A
!expr
-expr
Logical not
Unary minus
Numeric
Numeric
N/A
N/A
expr + expr
expr - expr
Addition
Subtraction
Numeric
Numeric
Left
Left
expr expr
String concatenation
String
Right
expr < expr
expr <= expr
expr != expr
expr == expr
expr > expr
expr >= expr
Less than
Less than or equal to
Not equal to
Equal to
Greater than
Greater than or equal to
Numeric
Numeric
Numeric
Numeric
Numeric
Numeric
None
None
None
None
None
None
expr ~ pat
expr !~ pat
(pat must be a literal, not an expression, e.g. /foo/)
Substring match
No substring match
Numeric
Numeric
None
None
expr && expr
Logical AND
Numeric
Left
expr || expr
Logical OR
Numeric
Left
expr1 ? expr2 : expr3
Conditional expression
Type of selected expr2 or expr3
Right
The following built-in functions are supported:
substr(s, m[, n])
Return the at most n-character substring of s that begins at
position m, numbering from 1. If n is omitted, or if n specifies
more characters than are left in the string, the length of the substring
will be limited by the length of the string s.
tolower(s)
Convert the string s to lower case.
toupper(s)
Convert the string s to upper case.
match(s, pat)
Return the position, in characters, numbering from 1, in string s where the
pattern pat occurs, or zero if it does not occur at all.
pat must be a literal, not an expression, e.g. /foo/.
length([s])
Return the length, in characters, of its argument taken as a string, or of the whole record, $0, if there is no argument.
Running tests in parallel
In order to run tests in parallel, you have to do the following:
Compile (or, more precisely, link) your test program with the -threaded
flag;
Launch the program with +RTS -N -RTS.
Timeout
To apply timeout to individual tests, use the --timeout (or -t) command-line
option, or set the option in your test suite using the mkTimeout function.
Timeouts can be fractional, and can be optionally followed by a suffix ms
(milliseconds), s (seconds), m (minutes), or h (hours). When there's no
suffix, seconds are assumed.
Example:
./test --timeout=0.5m
sets a 30 seconds timeout for each individual test.
Options controlling console output
The following options control behavior of the standard console interface:
-q,--quiet
Run the tests but don't output anything. The result is indicated only by the
exit code, which is 1 if at least one test has failed, and 0 if all tests
have passed. Execution stops when the first failure is detected, so not all
tests are necessarily run.
This may be useful for various batch systems, such as commit hooks.
--hide-successes
Report only the tests that has failed. Especially useful when the
number of tests is large.
-l,--list-tests
Don't run the tests; only list their names, in the format accepted by
--pattern.
--color
Whether to produce colorful output. Accepted values: never,
always, auto. auto means that colors will
only be enabled when output goes to a terminal and is the default value.
Custom options
It is possible to add custom options, too.
To do that,
Define a datatype to represent the option, and make it an instance of
IsOption
Register the options with the includingOptions ingredient
test.hs is where your main function is defined. The tests may be
contained in test.hs or spread across multiple modules (Mod1.hs, Mod2.hs,
...) which are then imported by test.hs.
Add the following section to the cabal file (my-project.cabal):
test-suite test
default-language:
Haskell2010
type:
exitcode-stdio-1.0
hs-source-dirs:
tests
main-is:
test.hs
build-depends:
base >= 4 && < 5
, tasty >= 0.7 -- insert the current version here
, my-project -- depend on the library we're testing
, ...
Tests for a program
All the above applies, except you can't depend on the library if there's no
library. You have two options:
Re-organize the project into a library and a program, so that both the
program and the test suite depend on this new library. The library can be
declared in the same cabal file.
Add your program sources directory to the Hs-source-dirs. Note that this
will lead to double compilation (once for the program and once for the test
suite).
Dependencies
Tasty executes tests in parallel to make them finish faster.
If this parallelism is not desirable, you can declare dependencies between
tests, so that one test will not start until certain other tests finish.
Dependencies are declared using the after combinator:
after AllFinish "pattern" my_tests will execute the test tree my_tests only after all
tests that match the pattern finish.
after AllSucceed "pattern" my_tests will execute the test tree my_tests only after all
tests that match the pattern finish and only if they all succeed. If at
least one dependency fails, then my_tests will be skipped.
The relevant types are:
after
::DependencyType--^ whether to run the tests even if some of the dependencies fail->String--^ the pattern->TestTree--^ the subtree that depends on other tests->TestTree--^ the subtree annotated with dependency informationdataDependencyType=AllSucceed | AllFinish
The pattern follows the same AWK-like syntax and semantics as described in
Patterns. There is also a variant named after_ that accepts the
AST of the pattern instead of a textual representation.
Let's consider some typical examples. (A note about terminology: here
by "resource" I mean anything stateful and external to the test: it could be a file,
a database record, or even a value stored in an IORef that's shared among
tests. The resource may or may not be managed by withResource.)
Two tests, Test A and Test B, access the same shared resource and cannot be
run concurrently. To achieve this, make Test A a dependency of Test B:
testGroup "Tests accessing the same resource"
[ testCase "Test A"$...
, after AllFinish"Test A"$
testCase "Test B"$...
]
Test A creates a resource and Test B uses that resource. Like above, we make
Test A a dependency of Test B, except now we don't want to run Test B if Test
A failed because the resource may not have been set up properly. So we use
AllSucceed instead of AllFinish
testGroup "Tests creating and using a resource"
[ testCase "Test A"$...
, after AllSucceed"Test A"$
testCase "Test B"$...
]
Here are some caveats to keep in mind regarding dependencies in Tasty:
If Test B depends on Test A, remember that either of them may be filtered out
using the --pattern option. Collecting the dependency info happens after
filtering. Therefore, if Test A is filtered out, Test B will run
unconditionally, and if Test B is filtered out, it simply won't run.
Tasty does not currently check whether the pattern in a dependency matches
anything at all, so make sure your patterns are correct and do not contain
typos. Fortunately, misspecified dependencies usually lead to test failures
and so can be detected that way.
Dependencies shouldn't form a cycle, otherwise Tasty with fail with the
message "Test dependencies form a loop." A common cause of this is a test
matching its own dependency pattern.
Using dependencies may introduce quadratic complexity. Specifically,
resolving dependencies is O(number_of_tests × number_of_dependencies),
since each pattern has to be matched against each test name. As a guideline,
if you have up to 1000 tests, the overhead will be negligible, but if you
have thousands of tests or more, then you probably shouldn't have more than a
few dependencies.
Additionally, it is recommended that the dependencies follow the
natural order of tests, i.e. that the later tests in the test tree depend on
the earlier ones and not vice versa. If the execution order mandated by the
dependencies is sufficiently different from the natural order of tests in the
test tree, searching for the next test to execute may also have an
overhead quadratic in the number of tests.
FAQ
Q: When my tests write to stdout/stderr, the output is garbled. Why is that and
what do I do?
A: It is not recommended that you print anything to the console when using the
console test reporter (which is the default one).
See #103 for the
discussion.
Use a test reporter that does not print to the console (like tasty-ant-xml).
Write your output to files instead.
Q: Why doesn't the --hide-successes option work properly? The test headings
show up and/or the output appears garbled.
A: This can happen sometimes when the terminal is narrower than the
output. A workaround is to disable ANSI tricks: pass --ansi-tricks=false
on the command line or set TASTY_ANSI_TRICKS=false in the environment.
Q: Patterns with slashes do not work on Windows. How can I fix it?
A: If you are running Git for Windows terminal, it has a habit of converting slashes
to backslashes. Set MSYS_NO_PATHCONV=1 to prevent this behaviour, or follow other
suggestions from Known Issues.
Press
Blog posts and other publications related to tasty. If you wrote or just found
something not mentioned here, send a pull request!
请发表评论