一、测试夹具(Test Fixtures):对多个测试使用相同的数据配置
如果你发现自己写了两个或更多的测试来操作类似的数据,你可以使用测试夹具。它允许您为几个不同的测试重复使用相同的对象配置。
要创建夹具,只需:
1.从:: testing :: Test派生一个类。 使用protected:或public:开始它的主体,因为我们想从子类 访问fixture成员。
2.在类中,声明你打算使用的任何对象。
3.如果需要,可以编写默认构造函数或SetUp()函数来为每个测试准备对象。 一个常见的错误是 拼写SetUp()为Setup()与一个小u -- 不要让这种情况发生在你身上。
4.如果需要,写一个析构函数或TearDown()函数来释放你在SetUp()中分配的任何资源。 要 学习什么时候应该使用构造函数/析构函数,当你应该使用SetUp()/ TearDown()时,请阅读这个 FAQ entry.。
5.如果需要,定义要分享的测试的子程序。
当使用夹具时,使用TEST_F()而不是TEST(),因为它允许您访问测试夹具中的对象和子程序:
TEST_F(test_case_name, test_name) { ... test body ... }?
和TEST()一样,第一个参数是测试用例名,但是对于TEST_F()必须是测试夹具类的名称。 你可能猜到了:_F是夹具。
不幸的是,C ++宏系统不允许我们创建一个可以处理两种类型的测试的宏。 使用错误的宏会导致编译器错误。
另外,在TEST_F()中使用它之前,你必须首先定义一个测试夹具类,否则将得到编译器错误“virtual outside class declaration”。
对于使用TEST_F()定义的每个测试,Google Test将:
1.在运行时创建一个新的测试夹具
2.立即通过SetUp()初始化,
3.运行测试
4.通过调用TearDown()清除
5.删除测试夹具。 请注意,同一测试用例中的不同测试具有不同的测试夹具对象,Google测试始 终会删除测试夹具,然后再创建下一个测试夹具。 Google测试不会为多个测试重复使用相同的 测试夹具。一个测试对夹具的任何更改不会影响其他测试。
例如,让我们为名为Queue的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; ... };
首先定义一个夹具类。按照惯例,你应该给它名称FooTest,其中Foo是被测试的类。
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_ *断言。 经验法则( The rule of thumb )是当你希望测试在断言失败后继续显示更多错误时使用EXPECT_ *,或是在失败后继续使用ASSERT_ *没有意义。 例如,Dequeue测试中的第二个断言是ASSERT_TRUE(n!= NULL),因为我们需要稍后解引用指针n,这将导致n为NULL时的segfault。
当这些测试运行时,会发生以下情况:
1.Google Test构造了一个QueueTest对象(我们称之为t1)。
2.t1.SetUp()初始化t1。
3.第一个测试(IsEmptyInitially)在t1上运行。
4.t1.TearDown()在测试完成后清理。
5.t1被析构。
6.以上步骤在另一个QueueTest对象上重复,这次运行DequeueWorks测试。
二、如何通过字夹具使多个测试用例重用一个测试夹具
1. 当定义测试夹具时,您指定将使用此夹具的测试用例的名称。 因此,测试夹具只能由一个测试用例使用。
有时,多个测试用例可能需要使用相同或稍微不同的测试夹具。 例如,您可能需要确保GUI库的所有测试不会泄漏重要的系统资源,如字体和画笔。 在Google测试中,您可以做到
这通过将共享逻辑放在超级(如“超级类”)测试夹具中,然后让每个测试用例使用从这个超级夹具派生的夹具。
在这个示例中,我们希望确保每个测试在?5秒内完成。 如果测试运行时间较长,我们认为测试失败。
我们把测试时间的代码放在一个叫做“QuickTest”的测试夹具中。 QuickTest旨在作为其他夹具派生的超级夹具,因此没有名为“QuickTest”的测试用例。
然后,我们将从QuickTest中导出多个测试夹具。
class QuickTest : public testing::Test { protected: // Remember that SetUp() is run immediately before a test starts. // This is a good place to record the start time. //这个方法在每一个test之前执行 virtual void SetUp() { start_time_ = time(NULL); } // TearDown() is invoked immediately after a test finishes. Here we // check if the test was too slow. //这个方法在每一个test之后执行 virtual void TearDown() { // Gets the time when the test finishes const time_t end_time = time(NULL); // Asserts that the test took no more than ~5 seconds. Did you // know that you can use assertions in SetUp() and TearDown() as // well? EXPECT_TRUE(end_time - start_time_ <= 5) << "The test took too long."; } // The UTC time (in seconds) when the test starts time_t start_time_; };
2.我们定义一个IntegerFunctionTest继承QuickTest, 使用该夹具的所有测试将自动要求快速。
class IntegerFunctionTest : public QuickTest { // We don‘t need any more logic than already in the QuickTest fixture. // Therefore the body is empty. };
3.现在我们可以在Integer Function Test测试用例中写测试了。
TEST_F(IntegerFunctionTest, Factorial) { // Tests factorial of negative numbers. EXPECT_EQ(1, Factorial(-5)); EXPECT_EQ(1, Factorial(-1)); EXPECT_GT(Factorial(-10), 0); // Tests factorial of 0. EXPECT_EQ(1, Factorial(0)); // Tests factorial of positive numbers. EXPECT_EQ(1, Factorial(1)); EXPECT_EQ(2, Factorial(2)); EXPECT_EQ(6, Factorial(3)); EXPECT_EQ(40320, Factorial(8)); }
4. 下一个测试用例(名为“QueueTest”)也需要很快,所以我们从QuickTest派生另一个夹具。
QueueTest测试夹具有一些逻辑和共享对象,除了QuickTest中已有的。 我们像往常一样在测试夹具的主体内定义额外的东西。
class QueueTest : public QuickTest { protected: virtual void SetUp() { // First, we need to set up the super fixture (QuickTest). QuickTest::SetUp(); // Second, some additional setup for this fixture. q1_.Enqueue(1); q2_.Enqueue(2); q2_.Enqueue(3); } // By default, TearDown() inherits the behavior of // QuickTest::TearDown(). As we have no additional cleaning work // for QueueTest, we omit it here. // // virtual void TearDown() { // QuickTest::TearDown(); // } Queue<int> q0_; Queue<int> q1_; Queue<int> q2_; };
接下来我们就可以用QueueTest写一些测试。
// Tests the default constructor. TEST_F(QueueTest, DefaultConstructor) { EXPECT_EQ(0u, q0_.Size()); }
如有必要,您可以从派生的夹具本身获得进一步的测试夹具。 例如,您可以从QueueTest派生另一个夹具。 Google测试对层次结构的深度没有限制。 然而,在实践中,你可能不希望它太深以至于混淆。