Crates.io | test-dsl |
lib.rs | test-dsl |
version | 0.4.0 |
created_at | 2025-03-25 10:19:49.533459+00 |
updated_at | 2025-04-03 09:15:25.445533+00 |
description | A helper crate to define easy-to-author tests as KDL documents |
homepage | |
repository | https://github.com/TheNeikos/test-dsl |
max_upload_size | |
id | 1605037 |
size | 114,033 |
cargo add --dev test-dsl
test-dsl
allows you define a set of verbs and conditions, to more easily
concentrate on authoring tests.
Instead of copy-pasting boilerplate and creating hard-to-read tests, this crate allows you to distill the behaviour of your library or application into small actions called 'verbs'.
An example test for an imaginary "http client" crate could look like this:
testcase {
create_mock_server "example.com"
create_client "sut"
connect client="sut" server="example.com"
assert {
check_last_connection status=200
verify_cache client="sut" url="example.com"
}
}
Using test-dsl
is straightforward:
For example, a fairly simple test-setup to check arithmetic can be defined as follows:
use std::sync::Arc;
use test_dsl::condition::FunctionCondition;
use test_dsl::verb::FunctionVerb;
use miette::NamedSource;
let mut ts = test_dsl::TestDsl::<usize>::new();
ts.add_condition("is_fortytwo", FunctionCondition::new_now(|h: &usize| Ok(*h == 42)));
ts.add_condition(
"is_equal",
FunctionCondition::new_now(|h: &usize, num: usize| Ok(*h == num)),
);
ts.add_verb(
"add",
FunctionVerb::new(|h: &mut usize, num: usize| {
*h += num;
Ok(())
}),
);
ts.add_verb(
"mul",
FunctionVerb::new(|h: &mut usize, num: usize| {
*h *= num;
Ok(())
}),
);
let testcases = ts
.parse_testcase(
r#"
testcase {
add 21
mul 2
assert {
is_fortytwo
}
}
testcase {
add 10
mul 10
assert {
is_equal 100
}
}
"#,
)
.unwrap();
// Check that its true
testcases[0].run(&mut 0).unwrap();
testcases[1].run(&mut 0).unwrap();
The following verbs come builtin:
repeat <number> { .. }
: it allows for repetition of a given block. Used as such:
testcase {
repeat 3 {
print "Hello World"
print "World Hello"
}
}
group { .. }
: it allows to group verbs together. Used as such:
testcase {
group {
print "Hello"
print "World"
}
}
NB: There is currently not much use to groups, but this may change in the future
assert { .. }
: it allows to assert a list of conditions. Used as such:
testcase {
send_message
assert {
message_was_sent
}
}
TestDsl
which serves as the coordinator.
Ideally you should have a single function creating this object that you can
reuse.
Each TestDsl
is generic over your test Harness. Which
is basically the 'coordinator' of your test. Think of it like an all-seeing
part of your system, that can kick-start functionalities you'd want to test.
It's usually best if your harness only interacts with the to-be-tested types
through their public functions. But depending on how you organize your code
it might also be able to access the inner workings.Verb
implementations. You can implement it yourself, or you can use
FunctionVerb
for quick in-line verb
definitions. FunctionVerb
accepts closures
which take your harness as well as arguments for your verb.Condition
s.
They allow for verifying your invariants. Similarly to verbs, you can
implement the trait yourself, or use the
FunctionCondition
helper.ParseArguments
is the bridge between
kdl
and test_dsl
. It allows verbs and conditions to accept input in form
of arguments and child nodes, and put it into a form that the
verbs/conditions can then make use of.VerbInstance
&
ConditionInstance
are both fully-parsed and
ready to run verbs & conditions. They are created from TestDsl
instances.
Mainly used in ParseArguments
implementations.