Google Test(GTest)使用方法和源码解析——自定义输出技术的分析和应用

        在介绍自定义输出机制之前,我们先了解下AssertResult类型函数。(转载请指明出于breaksoftware的csdn博客)

在函数中使用AssertionResult

        AssertionResult只有两种类型:

  1. AssertionSuccess()
  2. 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匹配到,从而被调用。

© 版权声明
THE END
喜欢就支持一下吧
点赞803 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容