#error #error-handling

failspot

A testing library that makes it easy(ish) to add intentional errors to a program

1 unstable release

0.2.0 Dec 6, 2024
0.1.0 Dec 6, 2024

#304 in Testing

Download history 192/week @ 2024-12-02 51/week @ 2024-12-09

243 downloads per month

MIT license

18KB
231 lines

failspot

crates.io docs.rs license

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