Boost logo

Boost :

Subject: Re: [boost] [test] Interest in 'hamcrest'-style checks?
From: Gennadiy Rozental (rogeeff_at_[hidden])
Date: 2013-09-11 00:12:11


Jared Grubb <jared.grubb <at> gmail.com> writes:

>
> I recently read "Modern C++ Programming with Test-Driven Development",

... All these books... trying to reinvent the wheel.

> and I really enjoyed this book and
> highly recommend it. One of the styles I saw for the first time was
> "hamcrest"-style assertions.
> ("Hamcrest" is an anagram of "matchers"; also see
> http://en.wikipedia.org/wiki/Hamcrest.)
>
> For a quick idea, imagine there is a BOOST_CHECK_THAT macro
> that looks like this: (excuse my choice of names
> here, we can pick better ones if people like the idea)
>
> BOOST_CHECK_THAT( str, IsEqualTo(expStr) );
> BOOST_CHECK_THAT( str, HasSubstring(needleStr) );
> BOOST_CHECK_THAT( str, Not(HasSubstring(badStr)) );
> BOOST_CHECK_THAT( vec, Contains(42) );
>
> Comparing them to current boost::test assertions, usually the
> two are directly equivalent, and it's just
> a matter of style. At first, I was unconvinced of a need for this.
> However, there are a few scenarios where
> the hamcrest-style actually are really helpful:
>
> BOOST_CHECK_THAT(vec, Any(Contains(42), Contains(-42)) );
> BOOST_CHECK_THAT(str, All(StartsWith("abc"),
> HasSubstring("lmnop"), EndsWith("xyz")) );
> BOOST_CHECK_THAT(count, All(GE(0), LT(10)) );
> BOOST_CHECK_THAT(vec, HasSubsequence(otherVec) );
> BOOST_CHECK_THAT(vec, Sorted(IsEqualTo({1, 2, 3})) );
> BOOST_CHECK_THAT(optionalStr, Any(IsEqualTo(boost::none),
> Deref(IsEqualTo("str")));

Hi Jared,

Let me start by saying that you might have something here, but I am not
convinced yet. I have number of comments here:

1. Stylistic
All these IsEqualTo, HasSubstring etc has a strong Java stench. I personally
do not see a place for something like this in proper C++.

2. Why exactly you are giving preference to first argument over all other
predicate arguments? Will your predicate report them all? Why the asymmetry
then?

3. Boost.Test already have this macro. It is called BOOST_CHECK_PREDICATE:

BOOST_CHECK_PREDICATE( P, (a)(b)(c) );

Very nice symmetry and most of your your other points covered (stackability
etc)

Moreover we have a framework for writing custom predicates. This should be a
function which returns test_tools::assertion_result. For example:

boost::test_tools::assertion_result
compare_lists( std::list<int> const& l1, std::list<int> const& l2 )
{
    if( l1.size() != l2.size() ) {
        boost::test_tools::predicate_result res( false );

        res.message() << "Different sizes [" << l1.size() << "!=" <<
l2.size() << "]";

        return res;
    }

    return true;
}

4. The new Boost.Test drops most of the current macros in a favor of a
single "super" macro BOOST_TEST:

BOOST_TEST( a == b )
BOOST_TEST( a != b )
BOOST_TEST( a+1 <= b )

This one is based on brilliant idea from Kevlin Henney (pretty much once of
the few rare new ideas that came out recently in testing domain). This
handles many of your cases already with proper syntax.

5. There way to many custom user specific predicates out there. We can try
all we want, but all we end up doing is just covering needs of rather small
subset of users. There should be line we draw somewhere: what is expected to
be supplied by the library and what is specific to the user's domain.

That being said...

If you believe you can present a set of some generic reusable predicates,
which can be used with BOOST_TEST_PREDICATE and implemented based on
assertion_result, I think we can discuss if there is a place for it inside
of UTF. Keep in mind "do not pay for what do not use" principle. You can't
have one big header with 100 dependencies.

Regards,
Gennadiy


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk