问题
I am using Boost Test to unit test some C++ code.
I have a vector of values that I need to compare with expected results, but I don't want to manually check the values in a loop:
BOOST_REQUIRE_EQUAL(values.size(), expected.size());
for( int i = 0; i < size; ++i )
{
BOOST_CHECK_EQUAL(values[i], expected[i]);
}
The main problem is that the loop check doesn't print the index, so it requires some searching to find the mismatch.
I could use std::equal
or std::mismatch
on the two vectors, but that will require a lot of boilerplate as well.
Is there a cleaner way to do this?
回答1:
Use BOOST_CHECK_EQUAL_COLLECTIONS. It's a macro in test_tools.hpp
that takes two pairs of iterators:
BOOST_CHECK_EQUAL_COLLECTIONS(values.begin(), values.end(),
expected.begin(), expected.end());
It will report the indexes and the values that mismatch. If the sizes don't match, it will report that as well (and won't just run off the end of the vector).
Note that if you want to use BOOST_CHECK_EQUAL
or BOOST_CHECK_EQUAL_COLLECTIONS
with non-POD types, you will need to implement
bool YourType::operator!=(const YourType &rhs) // or OtherType
std::ostream &operator<<(std::ostream &os, const YourType &yt)
for the comparison and logging, respectively.
The order of the iterators passed to BOOST_CHECK_EQUAL_COLLECTIONS
determines which is the RHS and LHS of the !=
comparison - the first iterator range will be the LHS in the comparisons.
回答2:
A bit off-topic, however, when sometimes one needs to compare collections of floating-point numbers using comparison with tolerance then this snippet may be of use:
// Have to make it a macro so that it reports exact line numbers when checks fail.
#define CHECK_CLOSE_COLLECTION(aa, bb, tolerance) { \
using std::distance; \
using std::begin; \
using std::end; \
auto a = begin(aa), ae = end(aa); \
auto b = begin(bb); \
BOOST_REQUIRE_EQUAL(distance(a, ae), distance(b, end(bb))); \
for(; a != ae; ++a, ++b) { \
BOOST_CHECK_CLOSE(*a, *b, tolerance); \
} \
}
This does not print the array indexes of mismatching elements, but it does print the mismatching values with high precision, so that they are often easy to find.
Example usage:
auto mctr = pad.mctr();
std::cout << "mctr: " << io::as_array(mctr) << '\n';
auto expected_mctr{122.78731602430344,-13.562000155448914};
CHECK_CLOSE_COLLECTION(mctr, expected_mctr, 0.001);
回答3:
How about BOOST_CHECK_EQUAL_COLLECTIONS?
BOOST_AUTO_TEST_CASE( test )
{
int col1 [] = { 1, 2, 3, 4, 5, 6, 7 };
int col2 [] = { 1, 2, 4, 4, 5, 7, 7 };
BOOST_CHECK_EQUAL_COLLECTIONS( col1, col1+7, col2, col2+7 );
}
example
Running 1 test case...
test.cpp(11): error in "test": check { col1, col1+7 } == { col2, col2+7 } failed.
Mismatch in a position 2: 3 != 4
Mismatch in a position 5: 6 != 7
* 1 failure detected in test suite "example"
回答4:
Since Boost 1.59 it is much easier to compare std::vector
instances. See this documentation for version 1.63 (which is nearly equal in this respect to 1.59).
For example if you have declared std::vector<int> a, b;
you can write
BOOST_TEST(a == b);
to get a very basic comparison. The downside of this is that in case of failure Boost only tells you that a
and b
are not the same. But you get more info by comparing element-wise which is possible in an elegant way
BOOST_TEST(a == b, boost::test_tools::per_element() );
Or if you want a lexicographic comparison you may do
BOOST_TEST(a <= b, boost::test_tools::lexicographic() );
回答5:
You can use BOOST_REQUIRE_EQUAL_COLLECTIONS with std::vector<T>
, but you have to teach Boost.Test how to print a std::vector
when you have a vector of vectors or a map whose values are vectors. When you have a map, Boost.Test needs to be taught how to print std::pair
. Since you can't change the definition of std::vector
or std::pair
, you have to do this in such a way that the stream insertion operator you define will be used by Boost.Test without being part of the class definition of std::vector
. Also, this technique is useful if you don't want to add stream insertion operators to your system under test just to make Boost.Test happy.
Here is the recipe for any std::vector
:
namespace boost
{
// teach Boost.Test how to print std::vector
template <typename T>
inline wrap_stringstream&
operator<<(wrap_stringstream& wrapped, std::vector<T> const& item)
{
wrapped << '[';
bool first = true;
for (auto const& element : item) {
wrapped << (!first ? "," : "") << element;
first = false;
}
return wrapped << ']';
}
}
This formats the vectors as [e1,e2,e3,...,eN]
for a vector with N
elements and will work for any number of nested vectors, e.g. where the elements of the vector are also vectors.
Here is the similar recipe for std::pair
:
namespace boost
{
// teach Boost.Test how to print std::pair
template <typename K, typename V>
inline wrap_stringstream&
operator<<(wrap_stringstream& wrapped, std::pair<const K, V> const& item)
{
return wrapped << '<' << item.first << ',' << item.second << '>';
}
}
BOOST_REQUIRE_EQUAL_COLLECTIONS
will tell you the index of the mismatched items, as well as the contents of the two collections, assuming the two collections are of the same size. If they are of different sizes, then that is deemed a mismatch and the differing sizes are printed.
来源:https://stackoverflow.com/questions/3999644/how-to-compare-vectors-with-boost-test