在介绍自定义输出机制之前,我们先了解下AssertResult类型函数。(转载请指明出于breaksoftware的csdn博客)
在函数中使用AssertionResult
AssertionResult只有两种类型:
- AssertionSuccess()
- AssertionFailure()
要么成功,要么失败,我们就可以使用基础断言来判断
::testing::AssertionResult IsEven(int n) {
if ((n % 2) == 0)
return ::testing::AssertionSuccess() << n << " is even";
else
return ::testing::AssertionFailure() << n << " is odd";
}
TEST(TestAssertResult, Check) {
EXPECT_FALSE(IsEven(0));
EXPECT_TRUE(IsEven(1));
}
我们在IsEven函数中输出了额外的内容(Actual中),便于我们之后查看结果
Value of: IsEven(0)
Actual: true (0 is even)
Expected: false
error: Value of: IsEven(1)
Actual: false (1 is odd)
Expected: true
自定义输出断言
如果默认的输出结果不能满足我们的需要,或者我们的类型不支持字符流输出,我们就需要自定义输出。我们可以使用
Fatal assertion | Nonfatal assertion | Verifies |
ASSERT_PRED_FORMAT1(pred_format1, val1); | EXPECT_PRED_FORMAT1(pred_format1, val1); | pred_format1(val1) is successful |
ASSERT_PRED_FORMAT2(pred_format2, val1, val2); | EXPECT_PRED_FORMAT2(pred_format2, val1, val2); | pred_format2(val1, val2) is successful |
… | … | … |
这一系列GTest也是有5对函数,最高是ASSERT/EXPECT_PRED_FORMAT5。我们看下使用的例子
::testing::AssertionResult IsEven2(const char* expr, int n) {
if ((n % 2) == 0)
return ::testing::AssertionSuccess() << expr << " = " << n << " is even";
else
return ::testing::AssertionFailure() << expr << " = " << n << " is odd";
}
TEST(TestAssertResult, Check2) {
int a = 0;
int b = 1;
EXPECT_PRED_FORMAT1(IsEven2, a);
EXPECT_PRED_FORMAT1(IsEven2, b);
}
我们发现,用于判断的表达式要求返回类型是AssertionResult。因为源码底层是
#define GTEST_ASSERT_(expression, on_failure) \\
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \\
if (const ::testing::AssertionResult gtest_ar = (expression)) \\
; \\
else \\
on_failure(gtest_ar.failure_message())
其次要求用于判断的表达式第一个参数要是一个const char*类型数据,它用于传递参数的名字。于是上面的测试输出是
error: b = 1 is odd
自定义类型输出
一些情况下,我们自定义类型可能是个复杂的符合结构。C++编译器并不知道怎么输出它,这个时候我们就需要告诉GTest如何去输出了。目前有两种方式
定义输出运算符函数
比如待测类是class Bar。我们只要定义一个方法
::std::ostream& operator<<(::std::ostream& os, const Bar& bar) {
return os << bar.DebugString(); // whatever needed to print bar to os
}
通过Bar暴露出来的方法将该对象输出。我们看一个例子
#include <vector>
#include <string>
using namespace std;
class Bar {
class Data {
public:
Data() {
strData = "17";
intData = 11;
}
public:
std::string strData;
int intData;
};
public :
std::string DebugString() const {
std::string output = "Bar.Data.strData = ";
output += data.strData;
output += "\\t";
output += "Bar.Data.intData = ";
char intBuffer[16] = {0};
itoa(data.intData, intBuffer, 10);
output += string(intBuffer);
return output;
}
Data data;
};
::std::ostream& operator<<(::std::ostream& os, const Bar& bar) {
return os << bar.DebugString(); // whatever needed to print bar to os
}
bool IsCorrectBarIntVector(vector<pair<Bar, int> > bar_ints) {
return false;
}
TEST(TestSelfDefineOutput, Test1) {
vector<pair<Bar, int> > bar_ints;
Bar bar;
bar_ints.push_back(pair<Bar, int>(bar, 1));
EXPECT_TRUE(IsCorrectBarIntVector(bar_ints))
<< "bar_ints = " << ::testing::PrintToString(bar_ints);
}
我们将Bar设计为一个较为复杂的结构,然后定义了一个函数DebugString用于输出其包含的变量。我们让断言进入出错状态,查看其输出
Actual: false
Expected: true
bar_ints = { (Bar.Data.strData = 17 Bar.Data.intData = 11, 1) }
可以看出来,GTest将Vector类型的数据格式化输出(使用了PrintToString方法),并使用我们自定义DebugString输出了自定义结构。
这儿有个有趣的地方,PrintToString的实现,比如它是如何判断它是个容器的
template <typename T>
void PrintTo(const T& value, ::std::ostream* os) {
DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), value, os);
}
typedef int IsContainer;
template <class C>
IsContainer IsContainerTest(int /* dummy */,
typename C::iterator* /* it */ = NULL,
typename C::const_iterator* /* const_it */ = NULL) {
return 0;
}
typedef char IsNotContainer;
template <class C>
IsNotContainer IsContainerTest(long /* dummy */) { return '\\0'; }
编译器遇到这种情况时,会试着用返回IsContainer的方法去匹配方法,但是如何发现class C没有迭代器,则用返回IsNotContaner的函数取匹配。这样就可以区分模板类是否是容器了。
还有个一is_pointer模板方法,用于判断是否是指针。
template <typename T>
struct is_pointer : public false_type {};
template <typename T>
struct is_pointer<T*> : public true_type {};
在我们的测试例子中,由于数据是个容器,且不是指针。那么将会匹配到
template <typename C>
void DefaultPrintTo(IsContainer /* dummy */,
false_type /* is not a pointer */,
const C& container, ::std::ostream* os) {
其实DefaultPrintTo方法还有其他两个,只是本次没有匹配到
template <typename T>
void DefaultPrintTo(IsNotContainer /* dummy */,
true_type /* is a pointer */,
T* p, ::std::ostream* os) {
template <typename T>
void DefaultPrintTo(IsNotContainer /* dummy */,
false_type /* is not a pointer */,
const T& value, ::std::ostream* os) {
定义PrintTo方法
有些时候,输出运算符可能被其他业务逻辑占用了。GTest就提供了一个针对性的方法,定义PrintTo方法,我们可以这么去做
void PrintTo(const Bar& bar, ::std::ostream* os) {
*os << bar.DebugString(); // whatever needed to print bar to os
}
那么它在什么时候被调用的呢?PrintToString最终会调到如下的函数中,
template <typename C>
void DefaultPrintTo(IsContainer /* dummy */,
false_type /* is not a pointer */,
const C& container, ::std::ostream* os) {
......
for (typename C::const_iterator it = container.begin();
it != container.end(); ++it, ++count) {
......
internal::UniversalPrint(*it, os);
}
......
}
template <typename T1, typename T2>
void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) {
*os << '(';
// We cannot use UniversalPrint(value.first, os) here, as T1 may be
// a reference type. The same for printing value.second.
UniversalPrinter<T1>::Print(value.first, os);
*os << ", ";
UniversalPrinter<T2>::Print(value.second, os);
*os << ')';
}
template <typename T>
class UniversalPrinter {
public:
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180)
static void Print(const T& value, ::std::ostream* os) {
PrintTo(value, os);
}
GTEST_DISABLE_MSC_WARNINGS_POP_()
};
其中UniversalPrinter<T1>::Print(value.first, os)会被我们定义的PrintTo匹配到,从而被调用。
暂无评论内容