[−][src]Crate diesel_factories
This is an implementation the test factory pattern made to work with Diesel.
Example usage:
#[macro_use] extern crate diesel; use diesel_factories::{Association, Factory}; use diesel::{pg::PgConnection, prelude::*}; // Tell Diesel what our schema is // Note unusual primary key name - see options for derive macro. mod schema { table! { countries (identity) { identity -> Integer, name -> Text, } } table! { cities (id) { id -> Integer, name -> Text, country_id -> Integer, } } } // Our city model #[derive(Clone, Queryable)] struct City { pub id: i32, pub name: String, pub country_id: i32, } #[derive(Clone, Factory)] #[factory( // model type our factory inserts model = City, // table the model belongs to table = crate::schema::cities, // connection type you use. Defaults to `PgConnection` connection = diesel::pg::PgConnection, // type of primary key. Defaults to `i32` id = i32, )] struct CityFactory<'a> { pub name: String, // A `CityFactory` is associated to either an inserted `&'a Country` or a `CountryFactory` // instance. pub country: Association<'a, Country, CountryFactory>, } // We make new factory instances through the `Default` trait impl<'a> Default for CityFactory<'a> { fn default() -> Self { Self { name: "Copenhagen".to_string(), // `default` will return an `Association` with a `CountryFactory`. No inserts happen // here. // // This is the same as `Association::Factory(CountryFactory::default())`. country: Association::default(), } } } // The same setup, but for `Country` #[derive(Clone, Queryable)] struct Country { pub identity: i32, pub name: String, } #[derive(Clone, Factory)] #[factory( model = Country, table = crate::schema::countries, connection = diesel::pg::PgConnection, id = i32, id_name = identity, )] struct CountryFactory { pub name: String, } impl Default for CountryFactory { fn default() -> Self { Self { name: "Denmark".into(), } } } // Usage fn basic_usage() { let con = establish_connection(); let city = CityFactory::default().insert(&con); assert_eq!("Copenhagen", city.name); let country = find_country_by_id(city.country_id, &con); assert_eq!("Denmark", country.name); assert_eq!(1, count_cities(&con)); assert_eq!(1, count_countries(&con)); } fn setting_fields() { let con = establish_connection(); let city = CityFactory::default() .name("Amsterdam") .country(CountryFactory::default().name("Netherlands")) .insert(&con); assert_eq!("Amsterdam", city.name); let country = find_country_by_id(city.country_id, &con); assert_eq!("Netherlands", country.name); assert_eq!(1, count_cities(&con)); assert_eq!(1, count_countries(&con)); } fn multiple_models_with_same_association() { let con = establish_connection(); let netherlands = CountryFactory::default() .name("Netherlands") .insert(&con); let amsterdam = CityFactory::default() .name("Amsterdam") .country(&netherlands) .insert(&con); let hague = CityFactory::default() .name("The Hague") .country(&netherlands) .insert(&con); assert_eq!(amsterdam.country_id, hague.country_id); assert_eq!(2, count_cities(&con)); assert_eq!(1, count_countries(&con)); } // Utility functions just for demo'ing fn count_cities(con: &PgConnection) -> i64 { use crate::schema::cities; use diesel::dsl::count_star; cities::table.select(count_star()).first(con).unwrap() } fn count_countries(con: &PgConnection) -> i64 { use crate::schema::countries; use diesel::dsl::count_star; countries::table.select(count_star()).first(con).unwrap() } fn find_country_by_id(input: i32, con: &PgConnection) -> Country { use crate::schema::countries::dsl::*; countries .filter(identity.eq(&input)) .first::<Country>(con) .unwrap() }
#[derive(Factory)]
Attributes
These attributes are available on the struct itself inside #[factory(...)]
.
Name | Description | Example | Default |
---|---|---|---|
model | Model type your factory inserts | City | None, required |
table | Table your model belongs to | crate::schema::cities | None, required |
connection | The connection type your app uses | MysqlConnection | diesel::pg::PgConnection |
id | The type of your table's primary key | i64 | i32 |
id_name | The name of your table's primary key column | identity | id |
These attributes are available on association fields inside #[factory(...)]
.
Name | Description | Example | Default |
---|---|---|---|
foreign_key_name | Name of the foreign key column on your model | country_identity | {association_name}_id |
Builder methods
Besides implementing Factory
for your struct it will also derive builder methods for easily customizing each field. The generated code looks something like this:
struct CountryFactory { pub name: String, } // This is what gets generated for each field impl CountryFactory { fn name<T: Into<String>>(mut self, new: T) -> Self { self.name = new.into(); self } } // So you can do this CountryFactory::default().name("Amsterdam");
Builder methods for associations
The builder methods generated for Association
fields are a bit different. If you have a factory like:
#[derive(Clone, Factory)] #[factory( model = City, table = crate::schema::cities, )] struct CityFactory<'a> { pub name: String, pub country: Association<'a, Country, CountryFactory>, }
You'll be able to call country
either with an owned CountryFactory
:
let country_factory = CountryFactory::default(); CityFactory::default().country(country_factory);
Or a borrowed Country
:
let country = Country { id: 1, name: "Denmark".into() }; CityFactory::default().country(&country);
This should prevent bugs where you have multiple factory instances sharing some association that you mutate halfway through a test.
Optional associations
If your model has a nullable association you can do this:
#[derive(Clone, Factory)] #[factory( model = User, table = crate::schema::users, )] struct UserFactory<'a> { pub name: String, pub country: Option<Association<'a, Country, CountryFactory>>, } impl<'a> Default for UserFactory<'a> { fn default() -> Self { Self { name: "Bob".into(), country: None, } } } // Setting `country` to a `CountryFactory` let country_factory = CountryFactory::default(); UserFactory::default().country(Some(country_factory)); // Setting `country` to a `Country` let country = Country { id: 1, name: "Denmark".into() }; UserFactory::default().country(Some(&country)); // Setting `country` to `None` UserFactory::default().country(Option::<CountryFactory>::None); UserFactory::default().country(Option::<&Country>::None);
Customizing foreign key names
You can customize the name of the foreign key for your associations like so
#[derive(Clone, Factory)] #[factory( model = City, table = crate::schema::cities, )] struct CityFactory<'a> { #[factory(foreign_key_name = country_id)] pub country: Association<'a, Country, CountryFactory>, }
Enums
Association | A "belongs to" association that may or may not have been inserted yet. |
Traits
Factory | A generic factory trait. |
Functions
sequence | Utility function for generating unique ids or strings in factories.
Each time |
Derive Macros
Factory |