我写了这个非常琐碎的课程,以便清楚我的问题是什么:
class A
{
public:
int x;
A(int y) {x=y;}
bool operator==(const A &other) const {return x==other.x;}
};
现在,如果我定义A first(1)和A second(1),那么BOOST_CHECK_EQUAL(第一个,第二个)应该通过似乎很自然。但是,在尝试这样做时我遇到了50个错误,第一个听起来像:运算符没有数学<<在ostr<<这是升级代码中的某个地方...其他测试工作得很好,比较已知类型甚至是指针,但是类对象似乎发生了一些不同的事情。
答案 0 :(得分:22)
我使用operator<<
确定了三种解决问题的方法。
第一种方法是为您的类型提供operator<<
。这是必需的,因为当boost_check_equal
失败时,它还会通过使用对象调用operator<<
来记录失败。请参阅休息后的详细附录,了解这是如何实现的。它比看起来更难。
第二种方法是不做我刚刚提到的日志记录。您可以#definine
BOOST_TEST_DONT_PRINT_LOG_VALUE
执行此操作。要仅针对一项测试禁用日志记录,您可以使用此#define
围绕相关测试,然后立即#undef
:
#define BOOST_TEST_DONT_PRINT_LOG_VALUE
BOOST_CHECK_EQUAL (first, second);
#undef BOOST_TEST_DONT_PRINT_LOG_VALUE
第三种方法是通过不将一个项目与另一个项目进行比较,而只是检查一个bool来回避对operator<<
适用于您的类型的需求:
BOOST_CHECK (first == second);
选择您喜欢的方法。
我的偏好是第一次,但实施这一点令人惊讶地具有挑战性。如果您只是在全局范围内定义operator<<
,那么它将无法工作。我认为这是因为名称解析问题。解决此问题的一个流行建议是将operator<<
放在std
命名空间中。这在某些编译器中至少在实践中有效,但我不喜欢它,因为标准禁止向std
命名空间添加任何内容。
我发现一种更好的方法是为您的类型实现自定义print_log_value
类模板特化。 print_log_value
是Boost.Test内部的类模板useb,实际上为指定的类型调用了正确的operator<<
。它委托operator<<
进行繁重的工作。专业print_log_value
为您的自定义类型由Boost [需要引证]正式支持,并由此完成。
假设您的类型名为Timestamp
(位于我的代码中),请先为operator<<
定义全局免费Timestamp
:
static inline std::ostream& operator<< (std::ostream& os, const Mdi::Timestamp& ts)
{
os << "Timestamp";
return os;
}
...然后为其提供print_log_value
专门化,委托给您刚刚定义的operator<<
:
namespace boost { namespace test_tools {
template<>
struct print_log_value<Mdi::Timestamp > {
void operator()( std::ostream& os,
Mdi::Timestamp const& ts)
{
::operator<<(os,ts);
}
};
}}
答案 1 :(得分:4)
根据John Dibling的回答,我一直在寻找一种方法来转换十六进制中的整数值,而不是小数我想出了这种方法:
// test_macros.h in my project
namespace myproject
{
namespace test
{
namespace macros
{
extern bool hex;
// context manager
struct use_hex
{
use_hex() { hex = true; }
~use_hex() { hex = false; }
};
}; // namespace
}; // namespace
}; // namespace
namespace boost
{
namespace test_tools
{
// boost 1.56+ uses these methods
template<>
inline
void
print_log_value<uint64>::
operator()(std::ostream & out, const uint64 & t)
{
if(myproject::test::macros::hex)
out << ::boost::format("0x%016X") % t;
else
out << t;
}
namespace tt_detail
{
// Boost < 1.56 uses these methods
template <>
inline
std::ostream &
operator<<(std::ostream & ostr, print_helper_t<uint64> const & ph )
{
if(myproject::test::macros::hex)
return ostr << ::boost::format("0x%016X") % ph.m_t;
return ostr << ph.m_t;
}
}; // namespace
}; // namespace
}; // namespace
现在在我的单元测试用例中,我可以通过设置全局静态bool值来打开/关闭十六进制,例如:
for(uint64 i = 1; i <= 256/64; ++i)
{
if(i % 2 == 0) test::macros::hex = true;
else test::macros::hex = false;
BOOST_CHECK_EQUAL(i+1, dst.pop());
}
我得到了我正在寻找的行为:
test_foobar.cc(106): error in "test_foobar_51": check i+1 == dst.pop() failed [2 != 257]
test_foobar.cc(106): error in "test_foobar_51": check i+1 == dst.pop() failed [0x0000000000000003 != 0x0000000000000102]
test_foobar.cc(106): error in "test_foobar_51": check i+1 == dst.pop() failed [4 != 259]
test_foobar.cc(106): error in "test_foobar_51": check i+1 == dst.pop() failed [0x0000000000000005 != 0x0000000000000104]
或者,我可以使用上下文管理器:
{
test::macros::use_hex context;
for(uint64 i = 1; i <= 4; ++i)
{
BOOST_CHECK_EQUAL(i + 0x200, i + 0x100);
}
}
for(uint64 i = 1; i <= 4; ++i)
{
BOOST_CHECK_EQUAL(i + 0x200, i + 0x100);
}
十六进制输出仅用于该块:
test_foobar.cc(94): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [0x0000000000000201 != 0x0000000000000101]
test_foobar.cc(94): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [0x0000000000000202 != 0x0000000000000102]
test_foobar.cc(94): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [0x0000000000000203 != 0x0000000000000103]
test_foobar.cc(94): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [0x0000000000000204 != 0x0000000000000104]
test_foobar.cc(100): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [513 != 257]
test_foobar.cc(100): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [514 != 258]
test_foobar.cc(100): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [515 != 259]
test_foobar.cc(100): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [516 != 260]
答案 2 :(得分:3)
这是优秀answer from John Dibling的补充。问题似乎是在正确的命名空间中需要一个输出操作符。因此,如果您定义了全局输出operator<<
,那么您可以通过在boost::test_tools::tt_detail
命名空间中定义另一个转发到的错误来避免此错误(至少使用Visual Studio 2015,即vc14和boost 1.60)全球运营商。这个小调整可以避免print_log_value
类的奇怪和更详细的特化。这是我做的:
namespace boost {
namespace test_tools {
namespace tt_detail {
std::ostream& operator<<(std::ostream& os, Mdi::Timestamp const& ts)
{
return ::operator<<(os, ts);
}
} // namespace tt_detail
} // namespace test_tools
} // namespace boost
虽然这个问题和答案已经过了三年,但我还没有在Boost.Test documentation中清楚地看到这个问题。
答案 3 :(得分:2)
有一种干净的方法可以通过自定义点启动Boost 1.64来记录用户定义的类型。可以找到此功能的完整文档here。
下面给出了文档中的一个例子。我们的想法是为您要打印的类型定义函数boost_test_print_type
,并将此函数放入测试用例(通过ADL找到):
#define BOOST_TEST_MODULE logger-customization-point
#include <boost/test/included/unit_test.hpp>
namespace user_defined_namespace {
struct user_defined_type {
int value;
user_defined_type(int value_) : value(value_)
{}
bool operator==(int right) const {
return right == value;
}
};
}
namespace user_defined_namespace {
std::ostream& boost_test_print_type(std::ostream& ostr, user_defined_type const& right) {
ostr << "** value of user_defined_type is " << right.value << " **";
return ostr;
}
}
BOOST_AUTO_TEST_CASE(test1)
{
user_defined_namespace::user_defined_type t(10);
BOOST_TEST(t == 11);
using namespace user_defined_namespace;
user_defined_type t2(11);
BOOST_TEST(t2 == 11);
}