1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
//! 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. //! //! [FactoryBot]: https://github.com/thoughtbot/factory_bot //! [`factori!()`]: macro.factori.html //! [`create!()`]: macro.create.html //! //! ## 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. //! //! [`tests/`]: https://github.com/mjkillough/factori/tree/master/tests //! //! ## 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. //! //! ``` //! # #[macro_use] extern crate factori; //! # fn main() { } //! # //! 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. //! //! [`cargo-expand`]: https://github.com/dtolnay/cargo-expand //! //! ## 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. // Clippy seems to get confused when testing procedural macros in doctests: #![allow(clippy::needless_doctest_main)] /// A macro to instantiate an instance of a factory. /// /// The type must already have had a factory defined using the [`factori!()`] /// macro. /// /// The `create!()` macro accepts: /// /// - The type to be instantiated using its factory. /// - Zero or more comma-separated mixins using the syntax `:name`. /// /// These are applied in the order that they are passed to `create!()`, /// which means that later mixins might override attributes already set by /// earlier mixins. /// /// You can think of the default values defined in the factory's `default` /// block as an implicit mixin which is always included first in every call /// to `create!()`. /// - Zero or more named fields with values, `field: value`. /// /// These override both the factory's default values and the provided /// mixins. Each field from the `default` block can appear zero or one /// times. /// /// # Example /// /// ``` /// # #[macro_use] extern crate factori; /// # /// struct Vehicle { /// registration: &'static str, /// number_wheels: u8, /// number_seats: u8, /// } /// /// factori!(Vehicle, { /// default { /// registration = "", /// number_wheels = 4, /// number_seats = 5, /// } /// /// mixin motorbike { /// number_wheels = 2, /// number_seats = 1, /// } /// /// mixin trike { /// number_wheels = 3 /// } /// }); /// /// fn main () { /// let trike = create!(Vehicle, :motorbike, :trike, registration: "J105 SRA"); /// assert_eq!(trike.number_wheels, 3); /// assert_eq!(trike.number_seats, 1); /// } /// ``` /// /// [`factori!()`]: macro.factori.html #[macro_export] macro_rules! create { // We define a simple macro so that the documentation doesn't state this // is a re-export from factori-impl. This also allows us to write docs here. ($($input:tt)*) => { $crate::factori_impl::create!($($input)*); } } #[doc(hidden)] pub use factori_impl; /// A macro to define a factory for a type. /// /// The macro accepts: /// /// - The type to be constructed by the factory. /// - A `default { }` block. /// /// This provides default values for all fields in the struct. /// - Zero or more `mixin name { }` blocks. /// /// These provide values to override the default values of one or more /// fields. They are typically used to define groups of values which allow /// you to quickly create test objects which are in certain states. /// /// Multiple mixin blocks can set the same attributes and the precedence is /// determined by the order that they are included in calls to [`create!()`]. /// /// [`create!()`]: macro.create.html /// /// ## Example /// /// ``` /// # #[macro_use] extern crate factori; /// # /// struct Order { /// id: u64, /// shipped: bool, /// } /// /// factori!(Order, { /// default { /// id = 1, /// shipped = false, /// } /// /// mixin shipped { /// shipped = true, /// } /// }); /// /// fn main() { /// let order = create!(Order, :shipped); /// } /// ``` /// /// ## Constructing complex types /// /// Under the hood, the example above constructs `Vehicle` using the struct /// literal syntax, passing the values defined in the `default` and `mixin` /// blocks. /// /// This isn't always possible, such as for types which can't be constructed /// with struct literal syntax (enums and tuple structs) or types with private /// fields. For these more complex types, a `builder` block can be provided to /// tell `factori!()` how to turn the fields in the `default` and `mixin` /// blocks into the factory's type. /// /// When a `builder` block is provided, the fields in `default` define an /// anonymous, temporary struct that is used during factory construction. To /// achieve this, the types of fields must be provided inside the `default` /// block. /// /// ``` /// # #[macro_use] extern crate factori; /// # /// pub struct Order(u64, bool); /// /// factori!(Order, { /// default { /// id: u64 = 1, /// shipped: bool = false, /// } /// /// builder { /// // All fields from default { } are in scope here with their values. /// // We construct a tuple struct here, but we could easily call a /// // method like Order::new(). /// Order(id, shipped) /// } /// /// mixin shipped { /// shipped = true, /// } /// }); /// /// fn main() { /// let order = create!(Order, :shipped, id: 2); /// } /// ``` #[macro_export] macro_rules! factori { // We define a simple macro so that the documentation doesn't state this // is a re-export from factori-impl. This also allows us to write docs here. ($($input:tt)*) => { $crate::factori_impl::define!($($input)*); } } #[doc(hidden)] pub trait Builder { type Ty; fn build(self) -> Self::Ty; } #[doc(hidden)] pub trait Default { fn default() -> Self; } #[doc(hidden)] pub trait Mixin<T> { fn default(self) -> T; fn extend(self, other: T) -> T; }