如果几乎/接近达到预期值,我想检查以下类型的对象。
class MyTypeWithDouble
{
public:
MyTypeWithDouble(double);
bool operator == (const MyTypeWithDouble& rhs) const; //Checks for Equality
private:
double m;
};
/////////////////////////////////////////
class MyTypeWithVector
{
public:
MyTypeWithVector(std::vector<double>v);
bool operator == (const MyTypeWithVector& rhs) const; //Checks for Equality
private:
std::vector<double> v;
};
因此单元测试看起来像这样
/// TEST1 ////////////////
MyTypeWithDouble t1(42.100001);
BOOST_CHECK_CLOSE(t1,42.1,0.1);
//////TEST2//////////////////////////////
std::vector<double> v; //no initalizer do not have c++11 :-(
v.push_back(42.1);
MyTypeWithVector t2(v);
std::vector<double> compare;
compare.push_back(42.100001);
MY_OWN_FUNCTION_USING_BOOST(t2,compare,0.1); //There is only BOOST_CHECK_EQUAL_COLLECTION available for collections
谢谢,ToBe
答案 0 :(得分:5)
我认为你是过度工程的。我建议一个简单的宏,也许有一个合适的朋友定义。那就是说,让我们接受挑战。
你的类型
check_is_close_t
实现的默认构造。 我们得到了
class MyTypeWithDouble
{
public:
constexpr MyTypeWithDouble(double v = 0) : m(v) {}
MyTypeWithDouble& operator=(MyTypeWithDouble const&) noexcept = default;
constexpr MyTypeWithDouble(MyTypeWithDouble const&) noexcept = default;
private:
friend class unittest_op::access;
double m;
};
通过一些繁琐的工作(在标题中?),我们可以使用这个access
漏洞来实现其他一切。 如何?好吧,我们定义了一个“getter”,好吧,但在类定义之外。
我在access
中定义了一个特质类模板(所以它隐式为friend
),你可以专注于你的“类似浮点”的类型:
namespace unittest_op {
template<> class access::impl<MyTypeWithDouble> {
public:
typedef double result_type;
static result_type call(MyTypeWithDouble const& v) { return v.m; }
};
}
这就是全部。嗯,这就是你作为类型/测试实现者的全部内容。当然,我们仍然需要做这项工作。
unittest_op
命名空间的唯一原因是定义“中继”操作符,这些操作符知道如何访问自定义类型中包含的值。
注意我们如何
2 * MyTypeWithDouble(7.0) -> MyTypeWithDouble(14.0)
)operator<<
,因此断言宏知道如何打印MyTypeWithDouble
感谢c++11,工作并不复杂:
namespace unittest_op {
class access {
template<typename T, typename Enable = void> class impl;
template<typename T>
class impl<T, typename std::enable_if<std::is_arithmetic<T>::value, void>::type>
{
public: typedef T result_type;
static T & call(T& v) { return v; }
static T const& call(T const& v) { return v; }
};
public:
template<typename T>
static typename impl<T>::result_type do_access(T const& v) { return impl<T>::call(v); }
template<typename T> static constexpr bool can_access(decltype(do_access(std::declval<T>()))*) { return true; }
template<typename T> static constexpr bool can_access(...) { return false; }
};
template<typename T>
typename std::enable_if<access::can_access<T>(nullptr) && not std::is_arithmetic<T>::value, std::ostream&>::type
operator<<(std::ostream& os, T const& v) { return os << "{" << access::do_access(v) << "}"; }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator-(T const& lhs) { return - access::do_access(lhs); }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator+(T const& lhs) { return + access::do_access(lhs); }
#define UNITTEST_OP_BINOP(OP) \
template <typename T1, typename T2> \
static decltype(access::do_access(std::declval<T1>()) OP access::do_access(std::declval<T2>())) \
operator OP(T1 const& lhs, T2 const& rhs) { return access::do_access(lhs) OP access::do_access(rhs); } \
using ::unittest_op::operator OP;
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
// assign-ops only for lvalue types (i.e. identity `access::impl<T>`)
UNITTEST_OP_BINOP(+=)
UNITTEST_OP_BINOP(-=)
UNITTEST_OP_BINOP(%=)
UNITTEST_OP_BINOP(*=)
UNITTEST_OP_BINOP(/=)
#undef UNITTEST_OP_BINOP
}
请注意,这些都是“开放”模板,我们采取了必要的预防措施,以确保只有do_access
定义为且< strong>该类型不是一个算术类型。
为何选择这些注意事项?
好。我们将进行强大的移动:我们将把运算符重载注入boost::test_tools
命名空间,以便BOOST_CHECK*
宏实现可以找到它们。
如果我们没有采取上面提到的预防措施,我们会因为操作员过载模糊而引起很多问题,因为我们并不关心这些类型。
强力抓取很简单:我们在using
命名空间内注入(boost::test_tools
)每个操作员模板。
现在我们很高兴:
<强> Live On Coliru 强>
BOOST_AUTO_TEST_CASE(my_test)
{
MyTypeWithDouble v(4);
BOOST_CHECK_CLOSE(3.99, v, MyTypeWithDouble(0.1));
}
打印
Running 2 test cases...
main.cpp(117): error in "my_test": difference{0.25%} between 3.99{3.9900000000000002} and v{{4}} exceeds {0.10000000000000001}%
<强> Live On Coliru 强>
#include <utility>
#include <type_traits>
#include <iostream>
namespace unittest_op {
class access {
template<typename T, typename Enable = void> class impl;
template<typename T>
class impl<T, typename std::enable_if<std::is_arithmetic<T>::value, void>::type>
{
public: typedef T result_type;
static T & call(T& v) { return v; }
static T const& call(T const& v) { return v; }
};
public:
template<typename T>
static typename impl<T>::result_type do_access(T const& v) { return impl<T>::call(v); }
template<typename T> static constexpr bool can_access(decltype(do_access(std::declval<T>()))*) { return true; }
template<typename T> static constexpr bool can_access(...) { return false; }
};
template<typename T>
typename std::enable_if<access::can_access<T>(nullptr) && not std::is_arithmetic<T>::value, std::ostream&>::type
operator<<(std::ostream& os, T const& v) { return os << "{" << access::do_access(v) << "}"; }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator-(T const& lhs) { return - access::do_access(lhs); }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator+(T const& lhs) { return + access::do_access(lhs); }
#define UNITTEST_OP_BINOP(OP) \
template <typename T1, typename T2> \
static decltype(access::do_access(std::declval<T1>()) OP access::do_access(std::declval<T2>())) \
operator OP(T1 const& lhs, T2 const& rhs) { return access::do_access(lhs) OP access::do_access(rhs); } \
using ::unittest_op::operator OP;
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
// assign-ops only for lvalue types (i.e. identity `access::impl<T>`)
UNITTEST_OP_BINOP(+=)
UNITTEST_OP_BINOP(-=)
UNITTEST_OP_BINOP(%=)
UNITTEST_OP_BINOP(*=)
UNITTEST_OP_BINOP(/=)
#undef UNITTEST_OP_BINOP
}
namespace boost { namespace test_tools {
using unittest_op::operator ==;
using unittest_op::operator !=;
using unittest_op::operator < ;
using unittest_op::operator > ;
using unittest_op::operator <=;
using unittest_op::operator >=;
using unittest_op::operator + ;
using unittest_op::operator - ;
using unittest_op::operator % ;
using unittest_op::operator * ;
using unittest_op::operator / ;
using unittest_op::operator +=;
using unittest_op::operator -=;
using unittest_op::operator %=;
using unittest_op::operator *=;
using unittest_op::operator /=;
using unittest_op::operator <<;
} }
class MyTypeWithDouble
{
public:
constexpr MyTypeWithDouble(double v = 0) : m(v) {}
MyTypeWithDouble& operator=(MyTypeWithDouble const&) noexcept = default;
constexpr MyTypeWithDouble(MyTypeWithDouble const&) noexcept = default;
private:
friend class unittest_op::access;
double m;
};
namespace unittest_op {
template<> class access::impl<MyTypeWithDouble> {
public:
typedef double result_type;
static result_type call(MyTypeWithDouble const& v) { return v.m; }
};
}
#define BOOST_TEST_MODULE MyTest
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_CASE(my_test)
{
MyTypeWithDouble v(4);
BOOST_CHECK_CLOSE(3.99, v, MyTypeWithDouble(0.1));
}
BOOST_AUTO_TEST_CASE(general_operator_invocations) // just a testbed to see the overloads are found and compile
{
MyTypeWithDouble v(4);
using namespace unittest_op; // we're not using the test_tools here
BOOST_CHECK(4.00000000000000001 == v);
BOOST_CHECK(4.000000000000001 != v);
#define UNITTEST_OP_BINOP(OP) { \
auto x = v OP static_cast<MyTypeWithDouble>(0.01); \
x = static_cast<MyTypeWithDouble>(0.01) OP v; \
x = v OP v; \
(void) x; \
}
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
//UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
-v == -v;
+v == +v;
}