posts - 319, comments - 22, trackbacks - 0, articles - 11
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

V1_6_Primer 入门谷歌 c + + 测试框架

Posted on 2011-09-26 20:28 RTY 阅读(363) 评论(0)  编辑 收藏 引用 所属分类: 质量保障
搜索 
 对于  
V1_6_Primer  
入门谷歌 c + + 测试框架
zhanyong...@gmail.com更新的2011 年 4 月 18 日

简介: 为什么谷歌 c + + 测试框架?

谷歌 c + + 测试框架可以帮助你写出更好的 c + + 测试。

无论是否您工作在 Linux 上,Windows 或 Mac,如果您编写 c + + 代码,谷歌测试可以帮助你。

是什么让一个好的测试,和谷歌 c + + 测试框架如何适应呢?我们相信:

  1. 测试应独立可重复它是痛苦调试成功或失败的其他测试结果的测试。谷歌 c + + 测试框架通过不同的对象上运行每个隔离测试。当测试失败时,谷歌 c + + 测试框架允许您在隔离的快速调试中运行它。
  2. 测试应举办井和反映测试代码的结构。谷歌 c + + 测试框架可以共享数据和子例程的测试用例入组相关的测试。这种常见模式很容易认出并使测试易于维护。这种一致性人切换项目,并开始一个新的代码库的工作时特别有用。
  3. 测试应携带可重复使用开放源代码社区有很多平台无关的代码,其测试也应平台无关。谷歌 c + + 测试框架在不同的操作系统,具有不同的编译器 (gcc、 MSVC,和其他人),有无异常,那么谷歌 c + + 测试框架的测试可以轻松地使用各种配置。(注意当前版本只包含生成脚本用于 Linux — — 我们正在积极地对其他平台的脚本)。
  4. 当测试失败时,他们应尽可能提供问题尽可能多的信息谷歌 c + + 测试框架不只是第一次测试失败。相反,它只停止当前测试,并继续下一步。您还可以设置测试该报告非致命故障后,当前测试继续。因此,您可以检测并修复单个编译编辑-运行周期中的多个 bug。
  5. 测试框架应解放测试作家从内务管理家务,让他们专注于测试的内容谷歌 c + + 测试框架自动跟踪的所有测试定义的并且不需要用户枚举它们为了运行它们。
  6. 测试应快速与谷歌 c + + 测试框架,可以在测试之间重用共享的资源并支付集上/泪下只有一次,而不做测试相互依赖。

由于谷歌 c + + 测试框架基于流行 xUnit 建筑,你会感觉右在家里如果您使用过 JUnit 或前的 PyUnit。如果不是,它将带你了解的基本知识和开始约 10 分钟。所以我们去吧 !

注:我们有时称为 Google c + + 测试框架非正式谷歌测试.

建立一个新的测试项目

要编写一个使用谷歌测试的测试程序,您需要将谷歌测试编译成库和链接您的测试与它。对于一些受欢迎的生成系统生成文件,我们提供: msvc /Visual studio, xcode /为 Mac Xcode,使 /为 GNU,指令 / Borland c + + 生成器,和 autotools 脚本 (已过时) 和CMakeLists.txt的 CMake (推荐) 谷歌测试的根目录中。如果您生成系统不在此列表中,您可以看一看学习 Google 测试应如何编译使/生成文件(基本上您要编译src/gtest-all.ccGTEST_ROOTGTEST_ROOT/包含在标题搜索路径中,其中GTEST_ROOT是 Google 测试根目录)。

一旦您将能够编译谷歌测试库,应创建一个项目,或您的测试程序生成目标。请确保你有GTEST_ROOT/包含标题搜索路径中,以便您的测试进行编译时,编译器可以找到"gtest/gtest.h" 。设置您的测试项目与谷歌测试库链接 (例如,在 Visual Studio 中,这通过在gtest.vcproj上添加依赖项).

如果您还有问题,看看如何测试 Google 测试生成并将其用作示例。

基本概念

在使用谷歌测试时,你开始通过编写断言,哪些语句,检查是否一个条件为真。断言结果可以成功非致命故障致命故障如果发生致命故障,它将中止当前的功能 ;否则,程序继续正常。

测试使用断言来验证测试的代码的行为。如果测试崩溃或已失败的断言,那么它将失败否则为它会成功.

测试用例包含一个或多个测试。您应将测试用例反映测试代码的结构组成您的测试。当多个测试的测试用例需要共同的对象和子例程时,你能把它们测试夹具的类。

测试程序可以包含多个测试用例。

我们现在将说明如何编写一个测试程序,开始在个别论断一级和建立测试和测试用例。

断言

谷歌测试断言是类似于函数调用的宏。您通过使其行为的断言测试类或函数。当断言失败时,谷歌测试打印断言的源文件和行号的位置,连同失败消息。您也可以提供自定义失败消息,将追加到谷歌测试邮件。

断言测试同样的事情,但有不同的效果,对当前函数的成对出现。ASSERT_ *版本生成失败时,致命故障和中止当前函数EXPECT_ *版本生成非致命的失败,不中止当前函数。EXPECT_ *通常首选,因为它们允许多个测试中报告故障。但是,您应该使用ASSERT_ *如果不合情理,则断言失败后继续。

由于失败ASSERT_ *从当前函数立即返回,可能会跳过清理代码后,它可能会导致空间泄漏。取决于漏油的性质,它可能会或可能不值得修复-所以记住这一点如果你除了断言错误堆检查器错误。

若要提供自定义失败消息,只是流它成宏使用<<运算符或这类商户的序列。示例:

ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";

for (int i = 0; i < x.size(); ++i) {
  EXPECT_EQ
(x[i], y[i]) << "Vectors x and y differ at index " << i;
}

任何可以传输到ostream的东西可以进行流式传输到断言宏 — — 尤其是 C 字符串和字符串对象。如果宽字符串 (wchar_t *, TCHAR *UNICODE模式下,在 Windows 中或std::wstring) 流入到一个断言,它将被翻译为 utf-8 打印时。

基本的断言

这些断言做真/假的基本条件测试。

致命的断言非致命的断言验证
ASSERT_TRUE (条件);EXPECT_TRUE (条件);条件为真
ASSERT_FALSE (条件);EXPECT_FALSE (条件);条件为假

请记住,当他们失败时, ASSERT_ *收益率出现致命故障和返回从当前函数,而EXPECT_ *产量为非致命的失败,从而能够继续运行功能。在任一情况下,断言失败意味着它包含测试失败。

可用性: Linux、 Windows、 mac。

二进制比较

本节介绍比较两个值的断言。

致命的断言非致命的断言验证
ASSERT_EQ (, 预期实际);EXPECT_EQ (, 预期实际);预期= =实际
ASSERT_NE (, val1 val2);EXPECT_NE (, val1 val2);val1! =val2
ASSERT_LT (, val1 val2);EXPECT_LT (, val1 val2);val1<val2
ASSERT_LE (, val1 val2);EXPECT_LE (, val1 val2);val1< =val2
ASSERT_GT (, val1 val2);EXPECT_GT (, val1 val2);val1>val2
ASSERT_GE (, val1 val2);EXPECT_GE (, val1 val2);val1> =val2

故障时,事件中谷歌测试打印在val1val2 。ASSERT_EQ * EXPECT_EQ * (及其他所有我们稍后介绍的平等断言),您应将您要测试的实际位置的表达式放并把其预期的价值放在预期,作为谷歌测试失败的消息非常适合于本公约。

值参数必须是可比的说法比较运算符的否则你会编译器错误。我们需要支持的参数使用<<以来 v1.6.0 运算符的流ostream,但它已不再需要 (如<<是支持,它将调用,则断言失败时打印参数 ; 否则将尝试将其打印出来的最佳方法,它可以在谷歌测试。更多详细信息以及如何自定义参数的打印,请参阅此 Google 模仿食谱.).

如果您定义相应的比较运算符,这些断言可以使用用户定义的类型,但仅限于 (例如= =, <,等等)。如果定义了相应的运算符,则更倾向于使用ASSERT_*()宏,因为他们会打印出结果的比较,但以及两个操作数不只。

参数总是进行一次评估。因此,它是有副作用的参数确定。然而,与任何普通的 C/c + + 函数,一样的参数的计算顺序是未定义 (即编译器是自由选择任何顺序) 和您的代码不应依赖于任何特定的参数计算顺序。

ASSERT_EQ()没有指针的指针平等。如果使用两个 C 字符串,它测试如果它们在相同的内存位置,如果它们具有相同的值。因此,如果您想通过值比较 C 字符串 (例如const char *),使用ASSERT_STREQ() ,将在稍后介绍的。特别是,声称是 C 字符串为,使用ASSERT_STREQ NULL c_string) 。不过,若要比较两个字符串对象,您应使用ASSERT_EQ.

这一节中的宏 (字符串wstring这两个狭窄和宽字符串对象的工作).

可用性: Linux、 Windows、 mac。

字符串比较

在此组中的断言,比较两个C 字符串如果要比较两个字符串对象,请使用EXPECT_EQ、 EXPECT_NE、 等代替。

致命的断言非致命的断言验证
ASSERT_STREQ (, expected_str actual_str);EXPECT_STREQ (, expected_str actual_str);两个 C 字符串具有相同的内容
ASSERT_STRNE (, str1 str2);EXPECT_STRNE (, str1 str2);两个 C 字符串具有不同的内容
ASSERT_STRCASEEQ (, expected_stractual_str);EXPECT_STRCASEEQ (, expected_stractual_str);两个 C 字符串具有相同的内容,忽略案例
ASSERT_STRCASENE (, str1 str2);EXPECT_STRCASENE (, str1 str2);两个 C 字符串具有不同的内容,忽略案例

请注意断言名称中的"案例"意味着忽略大小写。

* STREQ ** STRNE *也接受宽的 C 字符串 (wchar_t *)。如果两个宽字符串比较失败,其值将打印为 UTF 8 窄字符串。

一个空字符串和NULL指针被视为不同.

可用性: Linux、 Windows、 mac。

另请参阅: 更多的字符串比较特技 (子串、 前缀、 后缀,和正则表达式匹配,例如),请参见高级谷歌测试指南.

简单的测试

要创建一个测试:

  1. 使用TEST()宏来定义和测试函数命名,这些都是普通的 c + + 函数不会返回一个值。
  2. 在此函数中,与您要包括的任何有效的 c + + 语句一起使用各种谷歌测试断言检查值。
  3. 确定测试的结果,断言 ;如果在测试中的任何断言失败 (致命或非致命),或如果测试崩溃,整个测试失败。否则,它会成功。

TEST(test_case_name, test_name) {
 
... test body ...
}

TEST()参数从一般转到特定。第一个参数是测试用例的名称,第二个参数是内测试用例的测试的名称。这两个名称必须是有效的 c + + 标识符,它们不应包含下划线 (_)。测试的完整名称由其包含测试用例和其个人名称组成。来自不同测试用例的测试可以有个别的名称相同。

例如,让我们看一个简单的整数函数:

int Factorial(int n); // Returns the factorial of n

此函数的测试用例可能类似于:

// Tests factorial of 0.
TEST
(FactorialTest, HandlesZeroInput) {
  EXPECT_EQ
(1, Factorial(0));
}

// Tests factorial of positive numbers.
TEST
(FactorialTest, HandlesPositiveInput) {
  EXPECT_EQ
(1, Factorial(1));
  EXPECT_EQ
(2, Factorial(2));
  EXPECT_EQ
(6, Factorial(3));
  EXPECT_EQ
(40320, Factorial(8));
}

谷歌测试组的测试结果的测试用例,所以逻辑上相关的测试应在同一测试用例 ;换句话说,他们TEST()的第一个参数应是相同的。在上面的示例中,我们有两个测试, HandlesZeroInputHandlesPositiveInput,属于同一个测试用例FactorialTest.

可用性: Linux、 Windows、 mac。

测试夹具: 使用相同的数据配置多个测试

如果您发现自己相似的数据写入两个或更多的测试操作,您可以使用测试夹具这样,您就可以重复使用的对象的多个不同测试相同的配置。

若要创建夹具,只是:

  1. 派生类从:: testing::Test 。启动其体保护:公共: ,我们要从子类访问夹具成员。
  2. 在类中声明您计划使用的任何对象。
  3. 如有必要,写了默认的构造函数或SetUp()函数,准备每个测试的对象。一个常见的错误是,用一个小u - Setup()不要让发生在你身上的拼写SetUp() 。
  4. 如有必要,写了析构函数或TearDown()函数释放您在SetUp()中分配的任何资源。要了解何时应使用析构函数构造函数和何时应使用SetUp()/TearDown(),请阅读此FAQ 条目.
  5. 如果需要,定义您要共享的测试的子例程。

使用时夹具,使用TEST_F()TEST()而不是因为它允许您访问对象和测试夹具的子例程:

TEST_F(test_case_name, test_name) {
 
... test body ...
}

TEST(),第一个参数是测试用例的名称,但对于TEST_F() ,这必须测试夹具类的名称。您可能已经猜到: _F是夹具。

不幸的是,在 c + + 宏系统不允许我们能够创建一个单一的宏,可以处理这两种类型的测试。使用错误的宏将导致编译器错误。

此外,在TEST_F()中,在使用之前必须首先定义测试夹具类,否则你会编译器错误"' 虚拟类声明外'"。

TEST_F()定义的每个测试中,Google 测试将:

  1. 在运行时创建的测试夹具
  2. 立即将其初始化通过SetUp() ,
  3. 运行测试
  4. 通过调用TearDown()清理
  5. 删除测试夹具。请注意不同的测试,在同一个测试用例中有不同的测试夹具对象,创建下一个之前,谷歌测试总是删除测试夹具。谷歌测试不会重用相同的测试夹具,为多个测试。一个测试使夹具的任何更改不会影响其他测试。

作为一个例子,让我们写测试 FIFO 队列类命名队列中,具有以下接口:

template <typename E> // E is the element type.
class Queue {
 
public:
 
Queue();
 
void Enqueue(const E& element);
  E
* Dequeue(); // Returns NULL if the queue is empty.
  size_t size
() const;
 
...
};

首先,定义夹具的类。按照约定,,你应该给它生成器美孚在哪里类名称正在进行测试。

class QueueTest : public ::testing::Test {
 
protected:
 
virtual void SetUp() {
    q1_
.Enqueue(1);
    q2_
.Enqueue(2);
    q2_
.Enqueue(3);
 
}

 
// virtual void TearDown() {}

 
Queue<int> q0_;
 
Queue<int> q1_;
 
Queue<int> q2_;
};

在这种情况下,由于我们不需要每个测试,除已做的析构函数之后进行清理,则不需要TearDown() 。

现在我们会编写测试使用TEST_F()和这种装置。

TEST_F(QueueTest, IsEmptyInitially) {
  EXPECT_EQ
(0, q0_.size());
}

TEST_F
(QueueTest, DequeueWorks) {
 
int* n = q0_.Dequeue();
  EXPECT_EQ
(NULL, n);

  n
= q1_.Dequeue();
  ASSERT_TRUE
(n != NULL);
  EXPECT_EQ
(1, *n);
  EXPECT_EQ
(0, q1_.size());
 
delete n;

  n
= q2_.Dequeue();
  ASSERT_TRUE
(n != NULL);
  EXPECT_EQ
(2, *n);
  EXPECT_EQ
(1, q2_.size());
 
delete n;
}

上述使用了ASSERT_ *EXPECT_ *断言。经验法则是使用EXPECT_ *当您想要继续断言失败后,透露更多的错误,并使用ASSERT_ *时继续后失败没什么意义的测试。例如,出列测试中的第二个说法是ASSERT_TRUE (n! = NULL),就像我们要取消引用指针n后,这将导致 segfault nNULL.

当这些测试运行时,会发生下列情况:

  1. 谷歌测试构造让 (我们称之为t1 QueueTest对象 ).
  2. t1。SetUp()初始化t1 .
  3. T1上 ( IsEmptyInitially ) 的第一个测试运行 .
  4. t1。TearDown()测试完成后清理。
  5. t1被破坏。
  6. 这次运行的DequeueWorks测试上另一个QueueTest对象,重复上述步骤。

可用性: Linux、 Windows、 mac。

: 谷歌测试自动保存所有谷歌测试标志时测试对象构造,并还原它们时它破坏。

调用测试

TEST()TEST_F()隐式使用谷歌测试注册他们的测试。所以,不同于与很多其他 c + + 测试框架,你不必为了运行它们 re-list 定义的所有测试。

定义您的测试之后, 您可以运行它们的RUN_ALL_TESTS() ,它返回0 ,如果所有的测试都成功或1否则。请注意RUN_ALL_TESTS()运行您的链接单元中的所有测试— — 它们可以从不同的测试案例或甚至不同的源文件。

当调用, RUN_ALL_TESTS()宏:

  1. 保存所有谷歌测试标志的状态。
  2. 创建第一个测试夹具对象。
  3. 初始化它通过SetUp().
  4. 夹具对象上运行测试。
  5. 通过TearDown()的夹具清理.
  6. 删除夹具。
  7. 恢复所有谷歌测试标志的状态。
  8. 接下来的测试,重复上述步骤,直到所有的测试运行。

此外,如果文本夹具的构造函数生成致命故障在步骤 2 中,是没有意义的步骤 3-5,因此被跳过。同样,如果第 3 步生成一个致命的失败,则将被跳过第 4 步。

重要提示: 您一定不能忽略的返回值RUN_ALL_TESTS(),或将给你gcc编译器错误。这种设计的理由是自动化测试服务确定是否对其退出代码,不是在它的标准输出/stderr 输出 ; 测试已通过基于因此你的main ()函数必须返回值的RUN_ALL_TESTS().

此外,您应调用RUN_ALL_TESTS()唯一一次叫它不止一次冲突与一些高级功能 (如线程安全死亡测试) 的谷歌测试,因此不支持。

可用性: Linux、 Windows、 mac。

编写函数 main)

您可以从这个样板启动:

#include "this/package/foo.h"
#include "gtest/gtest.h"

namespace {

// The fixture for testing class Foo.
class FooTest : public ::testing::Test {
 
protected:
 
// You can remove any or all of the following functions if its body
 
// is empty.

 
FooTest() {
   
// You can do set-up work for each test here.
 
}

 
virtual ~FooTest() {
   
// You can do clean-up work that doesn't throw exceptions here.
 
}

 
// If the constructor and destructor are not enough for setting up
 
// and cleaning up each test, you can define the following methods:

 
virtual void SetUp() {
   
// Code here will be called immediately after the constructor (right
   
// before each test).
 
}

 
virtual void TearDown() {
   
// Code here will be called immediately after each test (right
   
// before the destructor).
 
}

 
// Objects declared here can be used by all tests in the test case for Foo.
};

// Tests that the Foo::Bar() method does Abc.
TEST_F
(FooTest, MethodBarDoesAbc) {
 
const string input_filepath = "this/package/testdata/myinputfile.dat";
 
const string output_filepath = "this/package/testdata/myoutputfile.dat";
 
Foo f;
  EXPECT_EQ
(0, f.Bar(input_filepath, output_filepath));
}

// Tests that Foo does Xyz.
TEST_F
(FooTest, DoesXyz) {
 
// Exercises the Xyz feature of Foo.
}

}  // namespace

int main(int argc, char **argv) {
 
::testing::InitGoogleTest(&argc, argv);
 
return RUN_ALL_TESTS();
}

:: Testing::InitGoogleTest()函数解析命令行,因为谷歌测试标志,并删除所有认可的标志。这允许用户控制通过各种标志,我们将介绍在AdvancedGuide的测试程序的行为。您必须在调用RUN_ALL_TESTS()之前, 调用此函数或标志不会被正确初始化。

在 Windows 上, InitGoogleTest()同样适用于宽字符串,因此它可以用于UNICODE模式以及在编译的程序。

但也许你认为编写所有那些 main () 函数是太多的工作吗?我们完全同意你,这就是为什么 Google 测试提供 main () 的基本实现。如果它适合您的需要,然后只是链接您的测试与 gtest_main 库,你是要走好。

对于 Visual c + + 用户的重要说明

如果你把你的测试放在图书馆,你的main ()函数是在另一个库或.exe 文件中将不会运行这些测试。原因是 Visual c + + 中的bug 。当您定义您的测试时,谷歌测试创建某些注册他们的静态对象。这些对象不从其他地方引用,但仍然应该运行它们的构造函数。Visual c + + 链接器看到什么库中引用的其他地方时它会引发图书馆。你要从主程序从丢弃它保持链接器的测试引用您的库。这里是如何做这件事。某处代码库中声明的函数:

__declspec(dllexport) int PullInMyLibrary() { return 0; }

如果你把你的测试放在静态库 (而不是 DLL) 然后__declspec(dllexport)则不需要。现在,在 main 程序中写一段代码,它调用该函数:

int PullInMyLibrary();
static int dummy = PullInMyLibrary();

这会让你的测试引用,会使他们自行注册在启动时。

此外,如果您在静态库中定义您的测试,添加/OPT:NOREF主程序链接器选项。如果您使用 MSVC + + IDE,转到您的.exe 项目属性/配置属性/链接器/优化和设置的引用设置为保持未引用数据 (/ 选择: NOREF)这将使 Visual c + + 链接器丢弃单个符号生成最终的可执行文件从您的测试。

不过还有一个更多陷阱。如果您使用谷歌测试静态库 (即如何定义的 gtest.vcproj) 作为您的测试也必须位于一个静态库。如果你要让他们在 DLL 中,您必须更改将建成一个 DLL 以及谷歌测试。否则为你的测试将不会正确运行,或者将不能运行。普遍的结论是: 使您的生活更轻松 — — 图书馆不写你的测试 !

从这里去哪里

恭喜 !您已经了解谷歌测试基础。可以开始编写和测试运行谷歌测试、 阅读一些样本,或继续AdvancedGuide,它描述了许多更有用的谷歌测试功能。

已知的局限性

谷歌测试被为了是线程安全的。执行是线程安全的pthreads图书馆在哪里可用的系统上。它目前是不安全使用谷歌测试断言从两个线程同时在其他系统 (如 Windows)。在大多数的测试中这不是问题,通常所断言的事,主线程。如果你想帮助,您可以在您的平台gtest port.h执行必要的同步基元志愿者。


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理