1 unstable release
0.2.0 | Dec 6, 2024 |
---|---|
0.1.0 |
|
#304 in Testing
243 downloads per month
18KB
231 lines
failspot
A testing library that makes it easy(ish) to add intentional errors to a program
When testing error-handling codepaths, it is often useful to programmatically tell parts of the code to fail. This
crate provides the failspot!()
macro, which can be used to mark a spot in the codepath where an intentional failure
can be toggled on and off from testing code.
lib.rs
:
A testing library that makes it easy(ish) to add intentional errors to a program
When testing error-handling codepaths, it is often useful to programmatically tell parts of the
code to fail. This crate provides the [failspot!()
][failspot] macro, which can be used
to mark a spot in the codepath where an intentional failure can be toggled on and off
from testing code.
Adding it to code is fairly simple:
fn read_data_file(path: &Path) -> Result<Vec<u8>, Box<dyn Error>> {
// `FailDataRead` is a variant of an enum that was declared in our crate
// `bail` returns `Err` and does type conversion on the error type
failspot!(FailDataRead bail(io::Error::other("failed due to test config")));
Ok(fs::read(path)?)
}
The [failspot_name!()
][failspot_name] macro is used to declare an enum that can be used
to name a failspot. Its syntax is identical to a regular enum declaration:
failspot_name! {
pub enum FailSpotName {
StuffWentWrong,
}
}
Syntaxes
The [failspot!()
][failspot] macro has four main syntaxes. Each takes either a
long form or a short form.
The long form explicitly contains the path to the enum containing the failspot names. This is useful if there are multiple enums with different failspot names, or if the enum is only accessible at a non-standard name or path.
If the enum can be reached at the name crate::FailSpotName
(either because it was declared in
the crate root or re-exported there), the short form versions of these macros
can be used. The examples below make this clear:
"if-else" syntax (long form)
Useful when standard if-else behavior is desired:
// In "long form", the entire path to the enum must be spelled out
let _enabled = failspot!(if <crate::my_module::MyFailName>::FailDataRead {
println!("Data read failure enabled");
true
} else {
println!("Data read failure disabled");
false
});
When the enabled
feature is not on, the compiler will see the second block verbatim.
The same code with the short-form
If an enum named FailSpotName
is reachable at the crate root, like this:
// lib.rs
failspot_name! {
pub enum FailSpotName {
FailDataRead,
}
}
The short form can be used, like this:
let _enabled = failspot!(if FailDataRead {
println!("Data read failure enabled");
true
} else {
println!("Data read failure disabled");
false
});
"quick expression" syntax
Useful when a short block of syntax should be evaluated when the failspot is enabled:
failspot!(FailDataRead println!("Data read failure enabled"));
// Also good for panicking
failspot!(FailDataRead panic!());
// Or for just early returning
failspot!(FailDataRead return);
// Multiple statements can be run, but use sparingly as things start to get ugly.
failspot!(FailDataRead println!("Data read failure enabled"); return);
When the enabled
feature is not on, the macro will evaluate to the empty block, {}
.
"bail" syntax
Useful for returning an Err
with error-type conversion:
fn main() -> Result<(), Box<dyn Error>> {
failspot!(FailDataRead bail(std::io::Error::other("Data read failure enabled")));
Ok(())
}
When the enabled
feature is not on, the macro will evaluate to the empty block, {}
.
"bool" syntax
Useful to just evaluate whether the failspot is enabled or not:
let fail_the_read = bytes_to_read > 5000 || failspot!(FailDataRead);
When the enabled
feature is not on, the macro will evaluate to the token false
.
Dependencies
~12KB