Expand description
A testing factory library for Rust, inspired by FactoryBot.
Factori aims to provide a clean, ergonomic syntax for instantiating test objects, without sacrificing type-safety.
This crate provides:
- A
factori!()
macro which is used to define factories. - A
create!()
macro which is used to instantiate objects from factories.
§Example
#[macro_use]
extern crate factori;
pub struct Vehicle {
number_wheels: u8,
electric: bool,
}
factori!(Vehicle, {
default {
number_wheels = 4,
electric = false,
}
mixin bike {
number_wheels = 2,
}
});
fn main() {
let default = create!(Vehicle);
assert_eq!(default.number_wheels, 4);
assert_eq!(default.electric, false);
// Its type is Vehicle, nothing fancy:
let vehicle: Vehicle = default;
let three_wheels = create!(Vehicle, number_wheels: 3);
assert_eq!(three_wheels.number_wheels, 3);
let electric_bike = create!(Vehicle, :bike, electric: true);
assert_eq!(electric_bike.number_wheels, 2);
assert_eq!(electric_bike.electric, true);
}
More examples are available in the tests/
alongside the crate.
§How it works
Behind the scenes, the factori!()
macro generates some extra types to
encode the default values and mixins for each factory.
The create!()
macro expects the generated _Factori
types to be in
scope. If the factory is instantiated in the same module that it is
defined, this will work as expected. If the factory is defined in a
separate module, then it is recommended that you do a glob import to bring
them into scope.
In most projects, you should expect to have a (or a few) factories
modules which contain shared factories. In tests
modules you can then use
a glob import to bring all of the required types into scope without
having them cluttering up your project’s namespaces.
struct Vehicle {
number_wheels: u8,
}
mod factories {
use super::Vehicle;
factori!(Vehicle, {
default {
number_wheels = 4
}
});
}
#[cfg(test)]
mod tests {
use super::{Vehicle, factories::*};
#[test]
fn some_test() {
let vehicle = create!(Vehicle);
assert_eq!(vehicle.number_wheels, 4);
}
}
The implementation details of the factori!()
and create!()
macros
are considered private and you should not rely on any of the generated
types or their names. However, the implementation is quite simple and you
are encouraged to run cargo-expand
in order to see the generated code.
The generated types are all prefixed with _Factori
and are unlikely to
clash with any types in your crate. It is a little gross but it is all
in the name of testing convenience.
§Error messages
The error messages coming from these macros are surprisingly good considering what they’re doing. However, if you encounter weird error messages that aren’t self-explanatory, please raise an issue on the GitHub repository.