Skip to content

How to combine quickcheck 1+ with fake? #320

Open

Description

@marktani

Following the zero2production book With quickcheck = "0.9.2", quickcheck_macros = "0.9.1" and fake = "~2.3", the following works, because the function Arbitrary::arbitrary expected a Gen trait, and Gen used to be a trait:

#[derive(Debug, Clone)]
struct ValidEmailFixture(pub String);

impl quickcheck::Arbitrary for ValidEmailFixture {
    fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Self {
        let email = SafeEmail().fake_with_rng(g);
        Self(email)
    }
}

#[quickcheck_macros::quickcheck]
fn valid_emails_are_parsed_successfully(valid_email: ValidEmailFixture) -> bool {
    SubscriberEmail::parse(valid_email.0).is_ok()
}

Then we pass the mutable reference g into SafeEmail().fake_with_rng(g); and it works - fake generates random valid emails.

However, in quickcheck = "1", quickcheck::Gen is no trait anymore, only a struct. I haven't found out how to access the inner rng: rand::rngs::SmallRng of Gen to pass it into SafeEmail().fake_with_rng and get fake to work with quickcheck.

How is it still possible in 1+?

Activity

  1. joshuamegnauth54 commented on Dec 23, 2023

    @joshuamegnauth54

    I've encountered the same issue. I solved it in a hacky way by reimplementing SafeEmail.

    #[cfg(test)]
    mod tests {
        use fake::locales::{self, Data};
        use quickcheck::{Arbitrary, Gen};
    
        #[derive(Debug, Clone)]
        struct ValidEmailFixture(pub String);
    
        impl Arbitrary for ValidEmailFixture {
            fn arbitrary(g: &mut Gen) -> Self {
                let username = g
                    .choose(locales::EN::NAME_FIRST_NAME)
                    .unwrap()
                    .to_lowercase();
                let domain = g.choose(&["com", "net", "org"]).unwrap();
                let email = format!("{username}@example.{domain}");
                Self(email)
            }
        }
    
        #[quickcheck_macros::quickcheck]
        fn valid_emails_are_parsed_successfully(email: ValidEmailFixture) -> bool {
            SubscriberEmail::parse(email.0).is_ok()
        }
    }

    Not ideal but also not too bad.

    Zero to Production's author, Luca Palmieri, posted an excerpt that covers OP's code above if anyone wants to follow along.

  2. npuichigo commented on Jan 25, 2024

    @npuichigo

    Same issue here @BurntSushi. How to use quickcheck together with other tools like fake which needs an RNG

  3. choco-green commented on Apr 30, 2024

    @choco-green

    I've encountered the same issue. I solved it in a hacky way by reimplementing SafeEmail.

    #[cfg(test)]
    mod tests {
        use fake::locales::{self, Data};
        use quickcheck::{Arbitrary, Gen};
    
        #[derive(Debug, Clone)]
        struct ValidEmailFixture(pub String);
    
        impl Arbitrary for ValidEmailFixture {
            fn arbitrary(g: &mut Gen) -> Self {
                let username = g
                    .choose(locales::EN::NAME_FIRST_NAME)
                    .unwrap()
                    .to_lowercase();
                let domain = g.choose(&["com", "net", "org"]).unwrap();
                let email = format!("{username}@example.{domain}");
                Self(email)
            }
        }
    
        #[quickcheck_macros::quickcheck]
        fn valid_emails_are_parsed_successfully(email: ValidEmailFixture) -> bool {
            SubscriberEmail::parse(email.0).is_ok()
        }
    }

    Not ideal but also not too bad.

    Zero to Production's author, Luca Palmieri, posted an excerpt that covers OP's code above if anyone wants to follow along.

    An extension for more context if you came here from the same book - This is exactly how fake is implmenting the safeEmail function.

    impl<L: Data + Copy> Dummy<SafeEmail<L>> for String {
        fn dummy_with_rng<R: Rng + ?Sized>(c: &SafeEmail<L>, rng: &mut R) -> Self {
            let username: String = FirstName(c.0).fake_with_rng::<&str, _>(rng).to_lowercase();
            let domain = ["com", "net", "org"].choose(rng).unwrap();
            format!("{}@example.{}", username, domain)
        }
    }

    For more information, there is #265 to read for why rng is no longer a dependency.

    Enough Digging, Enjoy coding :3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions