Skip to main content

Factory

Trait Factory 

Source
pub trait Factory {
    type Model: Model + Serialize + for<'r> FromRow<'r, SqliteRow> + for<'r> FromRow<'r, PgRow> + HydrateRelated;

    // Required method
    fn build() -> Self::Model;

    // Provided methods
    fn create<'async_trait>(    ) -> Pin<Box<dyn Future<Output = Result<Self::Model, FactoryError>> + Send + 'async_trait>>
       where Self: Send + 'async_trait { ... }
    fn create_with<'async_trait, F>(
        tweak: F,
    ) -> Pin<Box<dyn Future<Output = Result<Self::Model, FactoryError>> + Send + 'async_trait>>
       where F: FnOnce(&mut Self::Model) + Send + 'async_trait,
             Self: Send + 'async_trait { ... }
    fn create_batch<'async_trait>(
        n: usize,
    ) -> Pin<Box<dyn Future<Output = Result<Vec<Self::Model>, FactoryError>> + Send + 'async_trait>>
       where Self: Send + 'async_trait { ... }
}
Expand description

A factory for producing realistic instances of a model — the factory_boy / FactoryBot shape, in Rust.

You define a zero-sized marker type and point it at a Model through the associated type. The orphan rule is why the impl lives on a marker rather than on the model: in a downstream test crate both the model and this trait are foreign, so impl Factory for Plugin wouldn’t compile — but impl Factory for PluginFactory (a local marker) does.

use umbral_testing::{Factory, fake::{Fake, faker::{lorem::en::*, company::en::*}}, seq};

struct PluginFactory;
impl Factory for PluginFactory {
    type Model = Plugin;
    fn build() -> Plugin {
        let mut p = Plugin::default();
        p.name = CompanyName().fake();
        p.slug = format!("plugin-{}", seq());          // unique per call
        p.short_description = Sentence(4..8).fake();
        p
    }
}

// In a test, after `App::builder()...build()` has set the ambient pool
// and the tables exist:
let one      = PluginFactory::create().await?;                    // one row
let many     = PluginFactory::create_batch(5).await?;             // five rows
let featured = PluginFactory::create_with(|p| p.featured = true).await?;

build is pure (no I/O); the create* methods persist through the ORM against the ambient pool, so a built app must be in scope. Combine with TestClient to then exercise a handler against the rows the factory produced.

Required Associated Types§

Source

type Model: Model + Serialize + for<'r> FromRow<'r, SqliteRow> + for<'r> FromRow<'r, PgRow> + HydrateRelated

The model this factory produces. The bound set is exactly what #[derive(Model)] already provides on every model (the ORM’s create path needs Serialize + FromRow + HydrateRelated), so in practice you only ever write type Model = YourModel;.

Required Methods§

Source

fn build() -> Self::Model

A fresh, unsaved instance with realistic fake values. Pure — no database I/O. Override unique fields with seq so a batch doesn’t collide.

Provided Methods§

Source

fn create<'async_trait>() -> Pin<Box<dyn Future<Output = Result<Self::Model, FactoryError>> + Send + 'async_trait>>
where Self: Send + 'async_trait,

Build and persist one row through the ORM.

Source

fn create_with<'async_trait, F>( tweak: F, ) -> Pin<Box<dyn Future<Output = Result<Self::Model, FactoryError>> + Send + 'async_trait>>
where F: FnOnce(&mut Self::Model) + Send + 'async_trait, Self: Send + 'async_trait,

Build one row, apply tweak to override specific fields, then persist. This is the create(featured = true) override hook.

Source

fn create_batch<'async_trait>( n: usize, ) -> Pin<Box<dyn Future<Output = Result<Vec<Self::Model>, FactoryError>> + Send + 'async_trait>>
where Self: Send + 'async_trait,

Build and persist n rows.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§