woaidongmao

文章均收录自他人博客,但不喜标题前加-[转贴],因其丑陋,见谅!~
随笔 - 1469, 文章 - 0, 评论 - 661, 引用 - 0
数据加载中……

Google Test 框架高级使用指南

现在您已经读完 GoogleTestPrimer 并学会了如何用 Google Test 写一些测试程序, 现在是学习更多技巧的时候了。这篇文章将告诉你更多的断言、如何用流传递复杂的错误信息、传达致命失败、重用并加快你的测试夹具以及在你的测试工程中使用各种的标记。

现在您已经读完 GoogleTestPrimer 并学会了如何用 Google Test 写一些测试程序, 现在是学习更多技巧的时候了。这篇文章将告诉你更多的断言、如何用流传递复杂的错误信息、传达致命失败、重用并加快你的测试夹具以及在你的测试工程中使用各种的标记。

更多断言

本节包括一些不太常用的,但仍然很有意义的断言。 

明确声明成功或失败

这三个断言并不具体测试某个值或表达式,而是直接产生成功或失败。象其它用于测试的断言宏一样,你也可以为它们自定义失败信息。

SUCCEED();

产生一个成功断言,注意,这并不表示整个测试成功。一次测试成功的充分必要条件是:在整个测试期间没有出现失败的断言。

注:SUCCEED() 宏目前不会产生任何输出,实际上它只是被定义而已。不过,以后我们可能会为它加上自定义输出功能。

FAIL();

ADD_FAILURE();

FAIL* 生成一个致命失败,而 ADD_FAILURE* 则产生一个非致命失败。相对于布尔表达式,它们在控制流程时会更有用。例如,你可以象这样写:

1.                switch(expression) {

2.                  case 1: ... some checks ...

3.                  case 2: ... some other checks

4.                  ...

5.                  default: FAIL() << "We shouldn't get here.";

6.                }

switch(expression) {
  case 1: ... some checks ...
  case 2: ... some other checks
  ...
  default: FAIL() << "We shouldn't get here.";
}

适用于: Linux, Windows, Mac.
 

异常断言

以下断言用于测试一片代码是否会抛出指定类型的异常:

致命断言 

非致命断言 

验证项目 

ASSERT_THROW(statement, exception_type);

EXPECT_THROW(statement, exception_type);

statement 抛出类型为exception_type的异常

ASSERT_ANY_THROW(statement);

EXPECT_ANY_THROW(statement);

statement 抛出一个异常

ASSERT_NO_THROW(statement);

EXPECT_NO_THROW(statement);

statement 不会抛出异常

例如:

1.                ASSERT_THROW(Foo(5), bar_exception);

2.                 

3.                EXPECT_NO_THROW({

4.                  int n = 5;

5.                  Bar(&n);

6.                });

ASSERT_THROW(Foo(5), bar_exception);
 
EXPECT_NO_THROW({
  int n = 5;
  Bar(&n);
});

 

适用于: Linux, Windows, Mac; since version 1.1.0.
 

谓语断言

尽管 Google Test 有一系列丰富的断言,但它们永远不会足够,因为不可能(也不是好主意)去预计用户可能遇到的所有情况。因此,有时用户会使用 EXPECT_TRUE()来检查复杂的表达式,因为没有更好的宏可供选择。这样做有个问题就是它不能精确表达出你的期望和观点,当出现错误信息时难以理解。作为一种替代方法,一些用户会考虑用流产生自定义失败信息到EXPECT_TRUE()里。不管怎样,这种方法都令人尴尬尤其是目标表达式含有副作用或难以评价时。

为了解决这个问题,我们提供了一组通用的谓语断言:

致命断言

非致命断言 

验证项目 

ASSERT_PRED1(pred1, val1);

EXPECT_PRED1(pred1, val1);

pred1(val1) 返回 true

ASSERT_PRED2(pred2, val1, val2);

EXPECT_PRED2(pred2, val1, val2);

pred2(val1, val2) 返回 true

...

...

...

在上面的表格中,predn 是一个 n-个参数的谓语(函数或函数对象), val1, val2, ..., valn 是它的参数。如果在给定的参数下谓语返回真则测试成功,否则失败。当失败时,它会打印出每个参数。在任何情况下,参数只会被评估一次。

当前,我们只提供了最多支持5个参数的谓语断言,如果你需要更多的谓语断言,让我们知道。

举例来说,假定

1.                // Returns true iff m and n have no common divisors except 1.

2.                bool MutuallyPrime(int m, int n) { ... }

3.                const int a = 3;

4.                const int b = 4;

5.                const int c = 10;

// Returns true iff m and n have no common divisors except 1.
bool MutuallyPrime(int m, int n) { ... }
const int a = 3;
const int b = 4;
const int c = 10;

断言 EXPECT_PRED2(MutuallyPrime, a, b)成功,而 EXPECT_PRED2(MutuallyPrime, b, c)失败, 就会输出如下信息:

1.                MutuallyPrime(b, c) is false,

2.                where

3.                  b is 4

4.                  c is 10

MutuallyPrime(b, c) is false,
where
  b is 4
  c is 10

如果 (ASSERT|EXPECT)_PRED* 产生的信息不能满足你的要求,或一些参数不支持输出到流(ostream),你可以用下面的可以格式化自定义信息的断言来代替:

致命断言 

非致命断言 

验证项目

ASSERT_PRED_FORMAT1(pred_format1, val1);`

EXPECT_PRED_FORMAT1(pred_format1, val1);

pred_format1(val1) 成功

ASSERT_PRED_FORMAT2(pred_format2, val1, val2);

EXPECT_PRED_FORMAT2(pred_format2, val1, val2);

pred_format2(val1, val2) 成功

...

...

 

这组断言和上面的区别在于其谓语的区别,(ASSERT|EXPECT)_PRED_FORMAT* 使用 predicate-formatter (pred_formatn), 这是一个如下形式的函数或函数对象:

1.                testing::AssertionResult

2.                    PredicateFormattern(

3.                        const char* expr1, const char* expr2, ... const char* exprn, 

4.                        T1 val1, T2 val2, ... Tn valn); 

testing::AssertionResult
  PredicateFormattern(
     const char* expr1, const char* expr2, ... const char* exprn, 
     T1 val1, T2 val2, ... Tn valn); 

这里 val1, val2, ...valn 是其参数值,expr1, expr2, ...exprn 是相应源码中的表达式。类型T1, T2, ...Tn 可以是值类型也可以是参考类型。举例来说,如果函数参数类型是Foo,那么Foo或者const Foo&都可以作为这个谓语的参数。

predicate-formatter 返回testing::AssertionResult 对象来指出断言是成功还是失败。这个对象只能通过下面的工厂函数创建:

1.                namespace testing {

2.                 

3.                // Returns an AssertionResult object to indicate that an assertion has

4.                // succeeded.

5.                AssertionResult AssertionSuccess();

6.                 

7.                // Returns an AssertionResult object to indicate that an assertion has

8.                // failed with the given failure message.

9.                AssertionResult AssertionFailure(const Message& msg);

10.             

11.            // namespace testing

namespace testing {
 
// Returns an AssertionResult object to indicate that an assertion has
// succeeded.
AssertionResult AssertionSuccess();
 
// Returns an AssertionResult object to indicate that an assertion has
// failed with the given failure message.
AssertionResult AssertionFailure(const Message& msg);
 
}  // namespace testing

作为例子,我们来改善前面使用EXPECT_PRED2()的那个例子里的失败信息:

1.                // Returns the smallest prime common divisor of m and n,

2.                // or 1 when m and n are mutually prime.

3.                int SmallestPrimeCommonDivisor(int m, int n) { ... }

4.                 

5.                // A predicate-formatter for asserting that two integers are mutually prime.

6.                testing::AssertionResult AssertMutuallyPrime(const char* m_expr, const char* n_expr, int m, int n) {

7.                  if (MutuallyPrime(m, n))

8.                    return testing::AssertionSuccess();

9.                  testing::Message msg;

10.              msg << m_expr << " and " << n_expr << " (" << m << " and " << n

11.                  << ") are not mutually prime, " << "as they have a common divisor "

12.                  << SmallestPrimeCommonDivisor(m, n);

13.              return testing::AssertionFailure(msg);

14.            }

// Returns the smallest prime common divisor of m and n,
// or 1 when m and n are mutually prime.
int SmallestPrimeCommonDivisor(int m, int n) { ... }
 
// A predicate-formatter for asserting that two integers are mutually prime.
testing::AssertionResult AssertMutuallyPrime(const char* m_expr, const char* n_expr, int m, int n) {
  if (MutuallyPrime(m, n))
    return testing::AssertionSuccess();
  testing::Message msg;
  msg << m_expr << " and " << n_expr << " (" << m << " and " << n
      << ") are not mutually prime, " << "as they have a common divisor "
      << SmallestPrimeCommonDivisor(m, n);
  return testing::AssertionFailure(msg);
}

使用这个predicate-formatter, 我们用 EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c); 生成的消息是:b and c (4 and 10) are not mutually prime, as they have a common divisor 2.

你可能已经发现,我们以前提到的许多断言都只是(EXPECT|ASSERT)_PRED_FORMAT*的一个特例。实际上,它们中的大多数是(EXPECT|ASSERT)_PRED_FORMAT*的调用。

: 如果使用ASSERT_PRED* EXPECT_PRED*时出在编译错误"no matching function to call", 可以到GoogleTestFAQ 里找解决办法。

适用于: Linux, Windows, Mac.
 

浮点数比较

浮点数的比较是比较棘手的,由于舍入误差,两个浮点数精确匹配的可能性不大。因此,ASSERT_EQ 的直接比较是行不通的。鉴于浮点数可以表达一个相当宽的范围,使用固定误差也不是个好主意,更好的办法是使用相对误差,除非这个值本身就很接近0

一般来说,要合理地比较浮点数,就要仔细地选择误差范围。如果不想为这个伤神,使用ULPsUnits in the Last Place )作为默认值也不错,Google Test 就提供了这么做的断言。关于ULPs的详细解释那是相当的长,如果你有兴趣,请参阅 this article on float comparison.

浮点数比较宏

致命断言

非致命断言

验证项目

ASSERT_FLOAT_EQ(expected, actual);

EXPECT_FLOAT_EQ(expected, actual);

两个float近似相等 

ASSERT_DOUBLE_EQ(expected, actual);

EXPECT_DOUBLE_EQ(expected, actual);

两个double近似相等

这里的 "近似相等",意思是彼此差距在4 ULP's以内。

下面的断言允许你自己选择可接受的误差范围:

致命断言 

非致命断言 

验证项目 

ASSERT_NEAR(val1, val2, abs_error);

EXPECT_NEAR(val1, val2, abs_error);

val1val2之间的差距没有超出abs_error指定的绝对误差范围 

适用于: Linux, Windows, Mac.
 

浮点数的格式化谓语函数

一些浮点数操作还是很有用的,但并不是经常用到。为了避免再弄出一大堆新的宏,我们为其提供了格式化谓语函数,它们可以用在谓语断言(EXPECT_PRED_FORMAT2())里。

1.                EXPECT_PRED_FORMAT2(testing::FloatLE, val1, val2);

2.                EXPECT_PRED_FORMAT2(testing::DoubleLE, val1, val2);

EXPECT_PRED_FORMAT2(testing::FloatLE, val1, val2);
EXPECT_PRED_FORMAT2(testing::DoubleLE, val1, val2);

验证val1 小于或近似于 val2。你也可以用ASSERT_PRED_FORMAT2代替EXPECT_PRED_FORMAT2

适用于: Linux, Windows, Mac.
 

WindowsHRESULT断言

这些断言用于测试HRESULT成功或失败。

致命断言 

非致命断言 

验证项目 

ASSERT_HRESULT_SUCCEEDED(expression);

EXPECT_HRESULT_SUCCEEDED(expression);

expression 返回成功的 HRESULT

ASSERT_HRESULT_FAILED(expression);

EXPECT_HRESULT_FAILED(expression);

expression 返回失败的 HRESULT

其产生的信息是可读的错误信息,它依据的是HRESULT所对应的Windows错误信息。

你可以这样用: 

1.                CComPtr shell;

2.                ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application"));

3.                CComVariant empty;

4.                ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));

CComPtr shell;
ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application"));
CComVariant empty;
ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));

适用于: Windows.
 

断言的位置

你可以在任何C++代码里使用断言。但有一个限制,致命断言(FAIL* and ASSERT_*) 只能在无返回值的函数里使用,这是因为Google Test 没有使用异常机制。如果在有返回值的函数里使用致命断言你将会得到一个令人迷惑的编译错误:"error: void value not ignored as it ought to be"

如果你要在有返回值的函数里使用这种断言,一个方法是把这些函数改成无返回值的。如你可以把T2 Foo(T1 x)改成void Foo(T1x, T2* result),当然你得确定当函数中途退出时result里也有一个有效的值。现在它是无返回值的了,里面也就可以使用所有断言了。

如果你不想改变函数类型,那么你只能在里面使用非致命断言如ADD_FAILURE* EXPECT_*

注:依据C++语言规范,构造和析构函数并不是无返回值的函数,所以你也不能在那里使用致命断言。一个简单解决方法是把构造和析构函数体移到一个无返回值的私有函数里。不管怎样,你应当知道在构造代码里的致命断言并不会终止当前的测试,它只是造成更早地从构造中返回。这可能会使你的对象只构造了一部分,同样地,在析构中使用致命断言也可能会使对象析构不完全,所以在这些地方使用断言要小心

死亡测试(Death Tests)

在很多程序里,如果断言里的条件没有达到就会造成程序错误。这些合理的检查保证程序运行在一个正确的状态下,如果程序状态不正常则应该尽快断言失败。这些程序段的状态异常则往往意味着内存泄漏、安全漏洞或更糟。因此使这些程序段工作在预期状态下是极为重要的。

这些检测进程死亡原因的测试我们称之为死亡测试( death tests ) ,更一般地说,任何检查程序是否按预期退出的测试都可以称为death test

怎样写 Death Test

Google Test 使用下面的宏支持 death tests:

 

致命断言

非致命断言

验证项目

ASSERT_DEATH(statement, regex`);

EXPECT_DEATH(statement, regex`);

statement 以预期的错误崩溃

ASSERT_EXIT(statement, predicate, regex`);

EXPECT_EXIT(statement, predicate, regex`);

statement 以预期的错误退出并且错误码匹配predicate

这里的 statement 是一个可能造成进程退出的程序段, predicate 是一个函数或函数对象用于评估一个整型的进程退出码,regex 是一个正则表达式用于匹配stderr输出的错误信息。statement 可以是任何有效的代码段(包括复合语句)

通常, ASSERT 系列会退出当前函数而 EXPECT 系列不会。

Google Test定义了一些断言函数用于适应多数情形,它们都接受一个 int 参数并返回 bool,只有它返回true时才算Death Test成功:

testing::ExitedWithCode(exit_code)

如果程序正常退出并且退出码与exit_code相同则返回 true

testing::KilledBySignal(signal_number)

如果程序被signal_number信号kill的话就返回true

*_DEATH 宏是 *_EXIT 宏的包装,它用于检测进程以非0退出码退出或被一个信号杀死的情况。

要写Death Test,只要在你的测试函数里使用上面的这几个宏就可以了,例如:

1.                TEST(My*DeathTest*, Foo) {

2.                  // This death test uses a compound statement.

3.                  ASSERT_DEATH({ int n = 5; Foo(&n); }, "Error on line .* of Foo()");

4.                }

5.                TEST(MyDeathTest, NormalExit) {

6.                  EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), "Success");

7.                }

8.                TEST(MyDeathTest, KillMyself) {

9.                  EXPECT_EXIT(KillMyself(), testing::KilledBySignal(SIGKILL), "Sending myself unblockable signal");

10.            }

TEST(My*DeathTest*, Foo) {

  // This death test uses a compound statement.

  ASSERT_DEATH({ int n = 5; Foo(&n); }, "Error on line .* of Foo()");

}

TEST(MyDeathTest, NormalExit) {

  EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), "Success");

}

TEST(MyDeathTest, KillMyself) {

  EXPECT_EXIT(KillMyself(), testing::KilledBySignal(SIGKILL), "Sending myself unblockable signal");

}

检验项目:

·                     Foo(5) 使进程死亡并得到指定的信息。

·                     NormalExit() 使进程打印"Success" stderr并且退出码为0

·                     KillMyself() 使用信号 SIGKILL 杀死进程。

如果需要,这些测试函数也可以使用其它断言和代码。

重要信息: 我们强烈推荐你把包含死亡测试的测试用例(不是测试)命名成 *DeathTest 的形式,如上面的例子一样。在 死亡测试和线程会解释为什么要这么做。

适用于: Linux

它是怎么工作的

在内部, ASSERT_EXIT() 调用 fork() 来产生一个新进程。接下来这个子进程如何工作依赖于testing::FLAGS_gtest_death_test_style 的变量值(它能过命令行参数 --gtest_death_test_style来定义)

·                     如果这个值是 "fast",那么死亡测试马上开始。

·                     如果是 "threadsafe",子进程会重新执行所有的单元测试,就象原进程做的一样。

其它变量值是非法的并会使死亡测试失败。当前,它的默认值是"fast"。不过,我们保留在以后版本中改变它的权利,因此你的测试不应该依赖于它(默认值)。

不管哪种情况,父进程都会等待子进程完成,并检查:

1.                子进程退出码

2.                子进程向stderr输出的信息

如果死亡测试代码段没有引起进程退出而是一直执行完毕,子进程还是会被终止并且返回断言失败。

死亡测试和线程

在多线程执行之前fork进程将使死亡测试运行于单线程状态之下。有时,安排这样的环境是不可行的。举例来说,静态初始模块可以在线程启动甚至main()还没开始运行之前执行,一旦线程建立,就很难将其清理干净。

Google Test 有两个功能用于提示这个线程问题

1.                如果死亡测试开始时发现有多个线程在运行将会发出警告。

2.                "DeathTest"作为测试用例后缀的测试优先于其它测试执行。

在死亡测试代码段里建立线程是完全允许的,因为它们在一个子进程里执行因而不会影响到父进程。

Death Test Styles

引入"threadsafe" 死亡测试形式的目的是为了减少多线程环境里的测试问题。它会合理地添加一点运行时间来加强线程安全。不过我们还是建议使用"fast"形式除非你在测试时遇到了问题。

你可以在程序中设置死亡测试形式:

testing::FLAGS_gtest_death_test_style = "threadsafe";

你可以在 main() 里为所有的死亡测试设置测试形式,也可以为某次测试单独设置。Google Test会在每次测试之前保存这个标记并在测试完成后恢复,所以你不需要去管这部分工作,如下例:

1.                TEST(MyDeathTest, TestOne) {

2.                  testing::FLAGS_gtest_death_test_style = "threadsafe";

3.                  // This test is run in the "threadsafe" style:

4.                  ASSERT_DEATH(ThisShouldDie(), "");

5.                }

6.                 

7.                TEST(MyDeathTest, TestTwo) {

8.                  // This test is run in the "fast" style:

9.                  ASSERT_DEATH(ThisShouldDie(), "");

10.            }

11.             

12.            int main(int argc, char** argv) {

13.              testing::InitGoogleTest(&argc, argv);

14.              testing::FLAGS_gtest_death_test_style = "fast";

15.              return RUN_ALL_TESTS();

16.            }

TEST(MyDeathTest, TestOne) {

  testing::FLAGS_gtest_death_test_style = "threadsafe";

  // This test is run in the "threadsafe" style:

  ASSERT_DEATH(ThisShouldDie(), "");

}

 

TEST(MyDeathTest, TestTwo) {

  // This test is run in the "fast" style:

  ASSERT_DEATH(ThisShouldDie(), "");

}

 

int main(int argc, char** argv) {

  testing::InitGoogleTest(&argc, argv);

  testing::FLAGS_gtest_death_test_style = "fast";

  return RUN_ALL_TESTS();

}

忠告

ASSERT_EXIT()的参数可以是任何有效的C++代码,不过它不能从当前函数返回。也就是说这个代码段不能包含return或者一些会造成返回的宏(如ASSERT_TRUE())。如果这段代码在崩溃之前返回,那么Google Test就会打印出一条错误信息并返回测试失败。

因为代码运行在子进程里,一些内存的变化(如,改变变量,释放内存等)无法在父进程里观察到。这种情况下,如果你在死亡测试中释放了内在,你的程序将出现内存堆检查错误而父进程则一无所知。要解决这个问题,你可以:

1.                不要在死亡测试里释放内存。

2.                在父进程里再次释放内存。

3.                不要在程序中使用内存堆检查。

由于实现细节,你不能在一行中放多条死亡测试断言;不然将会显示出一条难以理解的编译错误。

尽管“threadsafe”增强了线程安全效果,一些如死锁之类的线程问题在使用pthread_atfork注册之前仍然会出现。以后版本中会改进这个问题。

因为threadsafe形式的死亡测试会调用argv[0]参数的execve函数来重新运行。如果程序中有更改当前工作目录的代码那么使用相对路径运行它时可能会出现找不到执行文件的情况。

在子程序中使用断言

给断言加上显影之尘”(trace)

如果一个测试子程序会被几个地方调用,那么当它断言失败时很难确定错误是在哪一次调用里发生的。你可以加上一些额外的记录或自定义失败信息来缓解这个问题,但通常会使测试变得复杂。一个更好的办法是使用SCOPED_TRACE宏:

 

SCOPED_TRACE(message);

这里的 message 可以是任何允许用 std::ostream 输出的数据(不一定要字符串)。这个宏会产生当前文件名,行号以及每个失败信息。其效果只在它的生存周期内有效。

例如,

10.            void Sub1(int n) {

11.                EXPECT_EQ(1, Bar(n));

12.                EXPECT_EQ(2, Bar(n + 1));

13.            }

14.             

15.            TEST(FooTest, Bar) {

16.                {

17.                    SCOPED_TRACE("A");  // 这个跟踪点信息会出现在所有属于它的有效范围内的失败断言里

18.                    Sub1(1);

19.                }

20.                // 这里不会出现跟踪点信息了

21.                Sub1(9);

22.            }

void Sub1(int n) {
  EXPECT_EQ(1, Bar(n));
  EXPECT_EQ(2, Bar(n + 1));
}
 
TEST(FooTest, Bar) {
  {
    SCOPED_TRACE("A");  // 这个跟踪点信息会出现在所有属于它的有效范围内的失败断言里
    Sub1(1);
  }
  // 这里不会出现跟踪点信息了
  Sub1(9);
}
 

结果输出会象这个样子:

path/to/foo_test.cc:11: Failure
Value of: Bar(n)
Expected: 1
  Actual: 2
   Trace:
path/to/foo_test.cc:17: A
 
path/to/foo_test.cc:12: Failure
Value of: Bar(n + 1)
Expected: 2
  Actual: 3

如果没有跟踪信息,将很难知道这两次失败是在那次调用里发生的。(你可以根据它的参数n为每个Sub1()加上额外的断言信息,只是这样有点乏味)

一些使用 SCOPED_TRACE 的提示

1.                一般来说只要输出信息适当的话,在子程序段开头的地方使用 SCOPED_TRACE 就足够了,而不用放到每个调用它的地方。

2.                当调用是在一个循环里时,把循环迭代器加入到SCOPED_TRACE里,这样你就能知道是哪次迭代造成的失败了。

3.                有时,使用行号就足以区分每次调用,那么你可以简单地直接使用空白串作为 SCOPED_TRACE 的参数。

4.                你可以嵌套使用 SCOPED_TRACE ,这时,所有的跟踪信息都会倒序地包含到失败信息中去。

5.                Emacs 里可以直接点击跟踪信息跳转到相对的代码行里!

适用于: Linux, Windows, Mac.

致命失败的传递

经常犯的失误是没有搞清 ASSERT_* FAIL* 失败仅仅是退出当前函数,而不是整个测试。举例来说,下面的测试是有问题的:

1.                void Subroutine() {

2.                  // 产生一个致命失败退出当前函数。

3.                  ASSERT_EQ(1, 2);

4.                  // 其下代码不会被调用。

5.                  ...

6.                }

7.                 

8.                TEST(FooTest, Bar) {

9.                  Subroutine();

10.              // 本想让Subroutine()产生一个致命失败而退出整个测试

11.              // 实际情况是:这个函数会继续执行下面的代码

12.              int* p = NULL;

13.              *p = 3; // Segfault!

14.            }

void Subroutine() {
  // 产生一个致命失败退出当前函数。
  ASSERT_EQ(1, 2);
  // 其下代码不会被调用。
  ...
}
 
TEST(FooTest, Bar) {
  Subroutine();
  // 本想让Subroutine()产生一个致命失败而退出整个测试
  // 实际情况是:这个函数会继续执行下面的代码
  int* p = NULL;
  *p = 3; // Segfault!
}

因为我们没有使用异常机制,所以这里不可能达成你想要的这个效果。为了缓解这个矛盾,Google Test提供了两个解决方案。你可以使用(ASSERT|EXPECT)_NO_FATAL_FAILURE 断言或 HasFatalFailure() 函数。

断言整个程序段

如上面所示,如果你调用一个含有 ASSERT_* 断言的子程序(并且测试失败),这次测试还是会继续下去直到结束,这可能不是你想要的。

通常人们希望致命失败能像异常一样传递出来。so Google Test 提供了下面的宏:

 

致命断言

非致命断言

验证项目

ASSERT_NO_FATAL_FAILURE(statement);

EXPECT_NO_FATAL_FAILURE(statement);

statement 不会产生致命失败(当前线程)。

只有断言和它处于同一线程时才会执行检查,如查statement 建立了一个新线程,那么这些线程里的所有失败都被忽略。

:

1.                ASSERT_NO_FATAL_FAILURE(Foo());

2.                 

3.                int i;

4.                EXPECT_NO_FATAL_FAILURE({

5.                  i = Bar();

6.                });

ASSERT_NO_FATAL_FAILURE(Foo());
 
int i;
EXPECT_NO_FATAL_FAILURE({
  i = Bar();
});

适用于: Linux, Windows, Mac. 当前不支持处理多线程里的断言.

在当前测试中检查致命失败

HasFatalFailure() testing::Test 类的静态成员函数,如果当前测试中曾遇到过致命失败则它返回 true 。这样就允许提早发现致命失败并退出。

1.                class Test {

2.                 public:

3.                  ...

4.                  static bool HasFatalFailure();

5.                };

6.                 

7.                典型的用法是模拟异常机制:

8.                 

9.                TEST(FooTest, Bar) {

10.              Subroutine();

11.              // Aborts if Subroutine() had a fatal failure.

12.              if (HasFatalFailure())

13.                return;

14.              // The following won't be executed.

15.              ...

16.            }

class Test {
 public:
  ...
  static bool HasFatalFailure();
};
 
典型的用法是模拟异常机制:
 
TEST(FooTest, Bar) {
  Subroutine();
  // Aborts if Subroutine() had a fatal failure.
  if (HasFatalFailure())
    return;
  // The following won't be executed.
  ...
}

如果 HasFatalFailure() TEST() , TEST_F() 或者测试夹具以外使用HasFatalFailure(),你要加上名空间 testing::Test:: 前缀,象这样:

1.                if (testing::Test::HasFatalFailure())

2.                  return;

if (testing::Test::HasFatalFailure())
  return;

适用于: Linux, Windows, Mac.

记录额外信息

你可以在测试代码中调用 RecordProperty("key", value) 来记录额外的信息,这里的 value 可以是C风格字符串或者是32位整数。当最后一个数据记录完成以后Google Test就会输出XML数据到你指定的地方,比如下面的测试:
 

1.                TEST_F(WidgetUsageTest, MinAndMaxWidgets) {

2.                  RecordProperty("MaximumWidgets", ComputeMaxUsage());

3.                  RecordProperty("MinimumWidgets", ComputeMinUsage());

4.                }


将会输出如下形式:
 

1.                ...

2.                  <testcase name="MinAndMaxWidgets" status="run" time="6" classname="WidgetUsageTest"

3.                            MaximumWidgets="12"

4.                            MinimumWidgets="9" />

5.                ...

注意:

·                     RecordProperty() Test 类的静态成员函数。因此在TEST或测试夹具外部使用它时就需要加上名空间前缀 testing::Test::

·                     key 必须是一个有效的XML属性名,而且不能和Google Test使用的重名(name,statustime classname)

 

在同一测试用例中共享资源

为了使每次测试之间保持无关性以及便于调试的原因,Google Test为每次测试建立一个新的测试夹具。但是,有时测试所使用的资源建立起来可能比较昂贵,这样为每次测试生成一个资源就显得过于奢侈了。

如果测试不会改变资源,那么共享这个资源就不会对测试带来问题。所以,对应于每次测试的设置与回收(set-up/tear-down)Google Test 也支持针对每个测试用例的设置与回收(set-up/tear-down)

1.                在你的测试夹具(比如 FooTest)中定义一些静态(static)成员变量来存放共享资源。

2.                同样在这个测试夹具中,定义 static void SetUpTestCase() 函数 (注意是SetUpTestCase不是SetupTestCase) 来设置共享资源,定义 static void TearDownTestCase() 函数来回收共享资源。

要做的只有这些了,Google Test会在运行第一个FooTest测试用例的测试前自动调用 SetUpTestCase() ,在运行最后一个FooTest测试用例的测试后 调用TearDownTestCase() ,在这之间,这个测试用例的所有测试都共享使用同一资源。

记住测试的顺序是不定的,所以你不能假设一个测试是在另一个测试之后运行。同样,测试也不能改变任何共享资源的状态,或者,改变了共享资源的状态后要在退出其控制范围前恢复。

举例:

1.                class FooTest : public testing::Test {

2.                protected:

3.                  // Per-test-case set-up.

4.                  // Called before the first test in this test case.

5.                  // Can be omitted if not needed.

6.                  static void SetUpTestCase() {

7.                    shared_resource_ = new ...;

8.                  }

9.                 

10.              // Per-test-case tear-down.

11.              // Called after the last test in this test case.

12.              // Can be omitted if not needed.

13.              static void TearDownTestCase() {

14.                delete shared_resource_;

15.                shared_resource_ = NULL;

16.              }

17.             

18.              // You can define per-test set-up and tear-down logic as usual.

19.              virtual void SetUp() { ... }

20.              virtual void TearDown() { ... }

21.             

22.              // Some expensive resource shared by all tests.

23.              static T* shared_resource_;

24.            };

25.             

26.            T* FooTest::shared_resource_ = NULL;

27.             

28.            TEST_F(FooTest, Test1) {

29.              ... you can refer to shared_resource here ...

30.            }

31.            TEST_F(FooTest, Test2) {

32.              ... you can refer to shared_resource here ...

33.            }

适用于: Linux, Windows, Mac.

全局设置与回收

正如你可以在测试级或测试用例级设置和回收资源一样,你也可以在测试程序级做这些事。

首先,你要继承 testing::Environment 类来定义一个测试环境,它负责设置与回收操作:

1.                class Environment {

2.                public:

3.                  virtual ~Environment() {}

4.                  // Override this to define how to set up the environment.

5.                  virtual void SetUp() {}

6.                  // Override this to define how to tear down the environment.

7.                  virtual void TearDown() {}

8.                };

然后,通过testing::AddGlobalTestEnvironment()函数向Google Test注册你的环境对象

Environment* AddGlobalTestEnvironment(Environment* env);

现在,当运行 RUN_ALL_TESTS() 时,它先会调用这个环境对象的 SetUp() 方法,然后开始测试,最后调用它的 TearDown() 方法。

注册多个环境对象是允许的,这种情况下,它们的SetUp()会按注册的顺序依次执行,最后它们的TearDown() 按相反顺序执行。

注意,Google Test自己也注册了一个环境对象,不要删除它。

你应该在运行RUN_ALL_TESTS()前使用AddGlobalTestEnvironment() ,一般会在 main()里。如果你使用 gtest_main,你就要在main()之前调用它。你可以通过定义全局变量的方法来做到:

testing::Environment* const foo_env = testing::AddGlobalTestEnvironment(new FooEnvironment);

不管怎样,我们还是强烈建议你自己写 main() 以及在main()里调用 AddGlobalTestEnvironment() 。依靠全局变量来做的话对使代码不易读,并且当你在多个单元中注册多个环境时无法确定它们的初始化顺序。

 

posted on 2009-05-02 01:45 肥仔 阅读(8847) 评论(0)  编辑 收藏 引用 所属分类: 库 & 代码段


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