rxpect 0.10.0

Extensible fluent expectations for Rust
Documentation
use crate::{CheckResult, ExpectProjection, Expectation, ExpectationBuilder};
use std::fmt::Debug;

/// Extension trait for equality expectations for iterables
pub trait IterableCountExpectations<'e, B>
where
    B: ExpectationBuilder<'e>,
{
    /// Make expectations on the number of items in the iterable.
    ///
    /// Don't call this on never-ending iterables.
    ///
    /// ```
    /// # use rxpect::expect;
    /// # use rxpect::expectations::iterables::IterableCountExpectations;
    /// # use rxpect::expectations::OrderExpectations;
    ///
    /// let items = vec!["bar", "foo", "foo"];
    /// expect(items).count().to_be_greater_than_or_equal(2);
    /// ```
    /// asserts that `items` contains at least 2 items
    fn count(self) -> impl ExpectationBuilder<'e, Value = usize>
    where
        Self: Sized;

    /// Expect an iterable to not be empty.
    ///
    /// ```
    /// # use rxpect::expect;
    /// # use rxpect::expectations::iterables::IterableCountExpectations;
    ///
    /// let items = vec!["bar", "foo", "foo"];
    /// expect(items).to_not_be_empty();
    /// ```
    /// asserts that `items` contains at least one item
    fn to_not_be_empty(self) -> Self;

    /// Expect an iterable to be empty.
    ///
    /// ```
    /// # use rxpect::expect;
    /// # use rxpect::expectations::iterables::IterableCountExpectations;
    ///
    /// let items: Vec<u8> = vec![];
    /// expect(items).to_be_empty();
    /// ```
    /// asserts that `items` contains no items
    fn to_be_empty(self) -> Self;
}

impl<'e, I, C, B> IterableCountExpectations<'e, B> for B
where
    I: Debug + 'e,
    for<'a> &'a I: IntoIterator<Item = &'a C>,
    C: Debug,
    B: ExpectationBuilder<'e, Value = I>,
{
    fn count(self) -> impl ExpectationBuilder<'e, Value = usize> {
        self.projected_by(|it: &I| Iterator::count(it.into_iter()))
    }

    fn to_not_be_empty(self) -> Self {
        self.to_pass(NotEmptyExpectation {})
    }

    fn to_be_empty(self) -> Self {
        self.to_pass(EmptyExpectation {})
    }
}

struct EmptyExpectation;

impl<I, C> Expectation<I> for EmptyExpectation
where
    I: Debug,
    for<'a> &'a I: IntoIterator<Item = &'a C>,
    C: Debug,
{
    fn check(&self, value: &I) -> CheckResult {
        if value.into_iter().next().is_none() {
            CheckResult::Pass
        } else {
            CheckResult::Fail(
                "Expected iterable to be empty, but it had at least one item".to_string(),
            )
        }
    }
}

struct NotEmptyExpectation;

impl<I, C> Expectation<I> for NotEmptyExpectation
where
    I: Debug,
    for<'a> &'a I: IntoIterator<Item = &'a C>,
    C: Debug,
{
    fn check(&self, value: &I) -> CheckResult {
        if value.into_iter().next().is_some() {
            CheckResult::Pass
        } else {
            CheckResult::Fail("Expected iterable to not be empty, but it was".to_string())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::IterableCountExpectations;
    use crate::expect;
    use crate::expectations::EqualityExpectations;
    use rstest::rstest;

    #[test]
    pub fn that_to_be_empty_accepts_empty_iterable() {
        // Given an empty vector
        let value: Vec<u32> = vec![];

        // Expect to_be_empty to pass
        expect(value).to_be_empty();
    }

    #[test]
    #[should_panic]
    pub fn that_to_be_empty_does_not_accept_non_empty_iterable() {
        // Given a non-empty vector
        let value = vec![1];

        // Expect to_be_empty to fail
        expect(value).to_be_empty();
    }

    #[test]
    pub fn that_to_not_be_empty_accepts_non_empty_iterable() {
        // Given a non-empty vector
        let value = vec![1];

        // Expect to_not_be_empty to pass
        expect(value).to_not_be_empty();
    }

    #[test]
    #[should_panic]
    pub fn that_to_not_be_empty_does_not_accept_empty_iterable() {
        // Given an empty vector
        let value: Vec<u32> = vec![];

        // Expect to_not_be_empty to fail
        expect(value).to_not_be_empty();
    }

    #[rstest]
    #[case(vec![])]
    #[case(vec![1])]
    #[case(vec![1, 2])]
    #[case(vec![1, 2, 3])]
    pub fn that_count_projects_correctly(#[case] items: Vec<u32>) {
        let expected_count = items.len();
        // Expect the count() projection to project the count of the iterable
        expect(items).count().to_equal(expected_count);
    }
}