您现在的位置:首页 >> 前端 >> 内容

AquickintroductiontotheGoogleC++TestingFramework

时间:2017/11/16 20:03:10 点击:

  核心提示:AquickintroductiontotheGoogleC++TestingFramework,这是一篇非常好的介绍谷歌测试框架基本知识的文章。我们知道谷歌在提供开源的模块的时候,往往都附带提供了相...

AquickintroductiontotheGoogleC++TestingFramework,这是一篇非常好的介绍谷歌测试框架基本知识的文章。我们知道谷歌在提供开源的模块的时候,往往都附带提供了相应的测试案例。例如浏览器的移植工作,理解这些测试案例,比如unit test案例是很重要的事情。本文介绍了谷歌基本的测试框架,对理解这些测试案例的意义很有帮助。

一 我们为啥要用gtest呢?

Why use the Google C++ Testing Framework?

There are many good reasons for you to use this framework. This section describes several of them.

1 强在多次测试的可行性:

Some categories of tests have bad memory problems that surface only during certain runs. Google's test framework provides excellent support for handling such situations. You can repeat the same test a thousand times using the Google framework. At the first sign of a failure, the debugger is automatically invoked. In addition, all of this is done with just two switches passed from command line:--gtest_repeat=1000 --gtest_break_on_failure.

Contrary to a lot of other testing frameworks, Google's test framework has built-in assertions that are deployable in software where exception handling is disabled (typically for performance reasons). Thus, the assertions can be used safely in destructors, too.

2 测试结果输出强大,到xml非常方便

Running the tests is simple. Just making a call to the predefinedRUN_ALL_TESTSmacro does the trick, as opposed to creating or deriving a separate runner class for test execution. This is in sharp contrast to frameworks such as CppUnit.

Generating an Extensible Markup Language (XML) report is as easy as passing a switch:--gtest_output="xml:". In frameworks such as CppUnit and CppTest, you need to write substantially more code to generate XML output.

二 举例,一个基本的测试构建

Creating a basic test

Consider the prototype for a simple square root function shown inListing 1.

构建一个简单的开根号的函数如下:

Listing 1. Prototype of the square root function

1double square-root (const double);

For negative numbers, this routine returns-1. It's useful to have both positive and negative tests here, so you do both.Listing 2shows that test case.

对应负数直接返回-1,下面是这个函数的测试

Listing 2. Unit test for the square root function

1 2 3 4 5 6 7 8 9 10 11 12#include "gtest/gtest.h" TEST (SquareRootTest, PositiveNos) { EXPECT_EQ (18.0, square-root (324.0)); EXPECT_EQ (25.4, square-root (645.16)); EXPECT_EQ (50.3321, square-root (2533.310224)); } TEST (SquareRootTest, ZeroAndNegativeNos) { ASSERT_EQ (0.0, square-root (0.0)); ASSERT_EQ (-1, square-root (-22.0)); }

Listing 2 creates a test hierarchy namedSquareRootTestand then adds two unittests,PositiveNosandZeroAndNegativeNos, to that hierarchy.

这里建立了两个单元测试,一个是测试正数,一个是0和负数

TESTis a predefined macro defined in gtest.h (available with the downloaded sources) that helps define this hierarchy.

测试用宏定义的方式已经在网页上了,

EXPECT_EQandASSERT_EQare also macros—in the former case test execution continues even if there is a failure while in the latter case test execution aborts. Clearly, if the square root of 0 is anything but 0, there isn't much left to test anyway. That's why theZeroAndNegativeNostest uses onlyASSERT_EQwhile thePositiveNostest usesEXPECT_EQto tell you how many cases there are where the square root function fails without aborting the test.

宏定义EXPECT_EQandASSERT_EQ的区别,一个只是告诉通知你有错,一个确实会中断测试的下一步进行。

三,运行测试

Running the first test

Now that you've created your first basic test, it is time to run it.Listing 3is the code for the main routine that runs the test.

Listing 3. Running the square root test

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17#include "gtest/gtest.h" TEST(SquareRootTest, PositiveNos) { EXPECT_EQ (18.0, square-root (324.0)); EXPECT_EQ (25.4, square-root (645.16)); EXPECT_EQ (50.3321, square-root (2533.310224)); } TEST (SquareRootTest, ZeroAndNegativeNos) { ASSERT_EQ (0.0, square-root (0.0)); ASSERT_EQ (-1, square-root (-22.0)); } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }

The::testing::InitGoogleTestmethod does what the name suggests—it initializes the framework and must be called beforeRUN_ALL_TESTS.RUN_ALL_TESTSmust be called only once in the code because multiple calls to it conflict with some of the advanced features of the framework and, therefore, are not supported. Note thatRUN_ALL_TESTSautomatically detects and runs all the tests defined using theTESTmacro. By default, the results are printed to standard output.Listing 4shows the output.

方法InitGoogleTest顾名思义就是初始化用的,而RUN_ALL_TESTS这只能自行一次。

Listing 4. Output from running the square root test

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20Running main() from user_main.cpp [==========] Running 2 tests from 1 test case. [----------] Global test environment set-up. [----------] 2 tests from SquareRootTest [ RUN ] SquareRootTest.PositiveNos ..\user_sqrt.cpp(6862): error: Value of: sqrt (2533.310224) Actual: 50.332 Expected: 50.3321 [ FAILED ] SquareRootTest.PositiveNos (9 ms) [ RUN ] SquareRootTest.ZeroAndNegativeNos [ OK ] SquareRootTest.ZeroAndNegativeNos (0 ms) [----------] 2 tests from SquareRootTest (0 ms total) [----------] Global test environment tear-down [==========] 2 tests from 1 test case ran. (10 ms total) [ PASSED ] 1 test. [ FAILED ] 1 test, listed below: [ FAILED ] SquareRootTest.PositiveNos 1 FAILED TEST

四,gtest的一些酷酷的功能。

Options for the Google C++ Testing Framework

InListing 3you see that theInitGoogleTestfunction accepts the arguments to the test infrastructure. This section discusses some of the cool things that you can do with the arguments to the testing framework.

4.1 测试输出格式选择

You can dump the output into XML format by passing--gtest_output="xml:report.xml"on the command line. You can, of course, replacereport.xmlwith whatever file name you prefer.

There are certain tests that fail at times and pass at most other times. This is typical of problems related to memory corruption. There's a higher probability of detecting the fail if the test is run a couple times. If you pass--gtest_repeat=2 --gtest_break_on_failureon the command line, the same test is repeated twice. If the test fails, the debugger is automatically invoked.

4.1 测试过滤,保证你不需要每次都全部做所有的测试,挑那些错误的做就好了

你也可以通过“-”,去挑选那些你不想做的测试。

Not all tests need to be run at all times, particularly if you are making changes in the code that affect only specific modules. To support this, Google provides--gtest_filter=. The format for the test string is a series of wildcard patterns separated by colons (:). For example,--gtest_filter=*runs all tests while--gtest_filter=SquareRoot*runs only theSquareRootTesttests. If you want to run only the positive unit tests fromSquareRootTest, use--gtest_filter=SquareRootTest.*-SquareRootTest.Zero*. Note thatSquareRootTest.*means all tests belonging toSquareRootTest, and-SquareRootTest.Zero*means don't run those tests whose names begin with Zero.

Listing 5provides an example of runningSquareRootTestwithgtest_output,gtest_repeat, andgtest_filter.

Listing 5. RunningSquareRootTestwithgtest_output,gtest_repeat, andgtest_filter

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42[arpan@tintin] ./test_executable --gtest_output="xml:report.xml" --gtest_repeat=2 -- gtest_filter=SquareRootTest.*-SquareRootTest.Zero* Repeating all tests (iteration 1) . . . Note: Google Test filter = SquareRootTest.*-SquareRootTest.Z* [==========] Running 1 test from 1 test case. [----------] Global test environment set-up. [----------] 1 test from SquareRootTest [ RUN ] SquareRootTest.PositiveNos ..\user_sqrt.cpp (6854): error: Value of: sqrt (2533.310224) Actual: 50.332 Expected: 50.3321 [ FAILED ] SquareRootTest.PositiveNos (2 ms) [----------] 1 test from SquareRootTest (2 ms total) [----------] Global test environment tear-down [==========] 1 test from 1 test case ran. (20 ms total) [ PASSED ] 0 tests. [ FAILED ] 1 test, listed below: [ FAILED ] SquareRootTest.PositiveNos 1 FAILED TEST Repeating all tests (iteration 2) . . . Note: Google Test filter = SquareRootTest.*-SquareRootTest.Z* [==========] Running 1 test from 1 test case. [----------] Global test environment set-up. [----------] 1 test from SquareRootTest [ RUN ] SquareRootTest.PositiveNos ..\user_sqrt.cpp (6854): error: Value of: sqrt (2533.310224) Actual: 50.332 Expected: 50.3321 [ FAILED ] SquareRootTest.PositiveNos (2 ms) [----------] 1 test from SquareRootTest (2 ms total) [----------] Global test environment tear-down [==========] 1 test from 1 test case ran. (20 ms total) [ PASSED ] 0 tests. [ FAILED ] 1 test, listed below: [ FAILED ] SquareRootTest.PositiveNos 1 FAILED TEST

五,屏蔽某些测试

Temporarily disabling tests

Let's say you break the code. Can you disable a test temporarily? Yes, simply add theDISABLE_ prefixto the logical test name or the inpidual unit test name and it won't execute.Listing 6demonstrates what you need to do if you want to disable thePositiveNostest fromListing 2.

Listing 6. Disabling a test temporarily

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15#include "gtest/gtest.h" TEST (DISABLE_SquareRootTest, PositiveNos) { EXPECT_EQ (18.0, square-root (324.0)); EXPECT_EQ (25.4, square-root (645.16)); EXPECT_EQ (50.3321, square-root (2533.310224)); } OR TEST (SquareRootTest, DISABLE_PositiveNos) { EXPECT_EQ (18.0, square-root (324.0)); EXPECT_EQ (25.4, square-root (645.16)); EXPECT_EQ (50.3321, square-root (2533.310224)); }

Note that the Google framework prints a warning at the end of the test execution if there are any disabled tests, as shown inListing 7.

Listing 7. Google warns user of disabled tests in the framework

1 21 FAILED TEST YOU HAVE 1 DISABLED TEST

If you want to continue running the disabled tests, pass the-gtest_also_run_disabled_testsoption on the command line.Listing 8shows the output when theDISABLE_PositiveNostest is run.

Listing 8. Google lets you run tests that are otherwise disabled

1 2 3 4 5 6 7 8 9 10[----------] 1 test from DISABLED_SquareRootTest [ RUN ] DISABLED_SquareRootTest.PositiveNos ..\user_sqrt.cpp(6854): error: Value of: square-root (2533.310224) Actual: 50.332 Expected: 50.3321 [ FAILED ] DISABLED_SquareRootTest.PositiveNos (2 ms) [----------] 1 test from DISABLED_SquareRootTest (2 ms total) [ FAILED ] 1 tests, listed below: [ FAILED ] SquareRootTest. PositiveNos

六 assertion说明

It's all about assertions

The Google test framework comes with a whole host of predefined assertions. There are two kinds of assertions—those with names beginning withASSERT_and those beginning withEXPECT_.

TheASSERT_*variants abort the program execution if an assertion fails whileEXPECT_*variants continue with the run. In either case, when an assertion fails, it prints the file name, line number, and a message that you can customize. Some of the simpler assertions includeASSERT_TRUE (condition)andASSERT_NE (val1, val2). The former expects the condition to always be true while the latter expects the two values to be mismatched. These assertions work on user-defined types too, but you must overload the corresponding comparison operator (==, !=, <=, and so on).

ASSERT_TRUE 条件真的判断

ASSERT_NE 判读两个变量是否相等

七 对应更高精度的要求下:

Floating point comparisons

Google provides the macros shown inListing 9for floating point comparisons.

Listing 9. Macros for floating point comparisons

1 2 3 4 5 6 7ASSERT_FLOAT_EQ (expected, actual) ASSERT_DOUBLE_EQ (expected, actual) ASSERT_NEAR (expected, actual, absolute_range) EXPECT_FLOAT_EQ (expected, actual) EXPECT_DOUBLE_EQ (expected, actual) EXPECT_NEAR (expected, actual, absolute_range)

Why do you need separate macros for floating point comparisons? Wouldn'tASSERT_EQwork? The answer is thatASSERT_EQand related macros may or may not work, and it's smarter to use the macros specifically meant for floating point comparisons. Typically, different central processing units (CPUs) and operating environments store floating points differently and simple comparisons between expected and actual values don't work. For example,ASSERT_FLOAT_EQ (2.00001, 2.000011)passes—Google does not throw an error if the results tally up to four decimal places. If you want greater precision, useASSERT_NEAR (2.00001, 2.000011, 0.0000001)and you receive the error shown inListing 10.

Listing 10. Error message fromASSERT_NEAR

1 2 3 4 5Math.cc(68): error: The difference between 2.00001 and 2.000011 is 1e-006, which exceeds 0.0000001, where 2.00001 evaluates to 2.00001, 2.000011 evaluates to 2.00001, and 0.0000001 evaluates to 1e-007.

八 退出消息比较和检查

Death tests

The Google C++ Testing Framework has an interesting category of assertions (ASSERT_DEATH,ASSERT_EXIT, and so on) that it calls thedeath assertions. You use this type of assertion to check if a proper error message is emitted in case of bad input to a routine or if the process exits with a proper exit code.

错误的测试结果,我们往往会定义一些错误代码。然而,最好的方式,还是给出错误的消息讯息。谷歌测试会允许你通过比较这些你自己定制话的错误讯息去assert测试的过程。

For example, inListing 3, it would be good to receive an error message when doingsquare-root (-22.0)and exiting the program with return status-1instead of returning-1.0.Listing 11usesASSERT_EXITto verify such a scenario.

Listing 11. Running a death test using Google's framework

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21#include "gtest/gtest.h" double square-root (double num) { if (num < 0.0) { std::cerr << "Error: Negative Input\n"; exit(-1); } // Code for 0 and +ve numbers follow } TEST (SquareRootTest, ZeroAndNegativeNos) { ASSERT_EQ (0.0, square-root (0.0)); ASSERT_EXIT (square-root (-22.0), ::testing::ExitedWithCode(-1), "Error: Negative Input"); } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }

ASSERT_EXITchecks if the function is exiting with a proper exit code (that is, the argument toexitor_exitroutines) and compares the string within quotes to whatever the function prints to standard error. Note that the error messages must go to

std::cerrand notstd::cout.Listing 12provides the prototypes forASSERT_DEATHandASSERT_EXIT.

Listing 12. Prototypes for death assertions

1 2ASSERT_DEATH(statement, expected_message) ASSERT_EXIT(statement, predicate, expected_message)

Google provides the predefined predicate::testing::ExitedWithCode(exit_code). The result of this predicate is true only if the program exits with the sameexit_codementioned in the predicate.ASSERT_DEATHis simpler thanASSERT_EXIT; it just compares the error message in standard error with whatever is the user-expected message.

Understanding test fixtures

这里是整个测试初始化的部分,结束部分。游离于测试实体之外的设计都在这里。

It is typical to do some custom initialization work before executing a unit test. For example, if you are trying to measure the time/memory footprint of a test, you need to put some test-specific code in place to measure those values. This is where fixtures come in—they help you set up such custom testing needs.Listing 13shows what a fixture class looks like.

Listing 13. A test fixture class

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21class myTestFixture1: public ::testing::test { public: myTestFixture1( ) { // initialization code here } void SetUp( ) { // code here will execute just before the test ensues } void TearDown( ) { // code here will be called just after the test completes // ok to through exceptions from here if need be } ~myTestFixture1( ) { // cleanup any pending stuff, but no exceptions allowed } // put in any custom data members that you need };

The fixture class is derived from the::testing::testclass declared ingtest.h.Listing 14is an example that uses the fixture class. Note that it uses theTEST_Fmacro instead ofTEST.

Listing 14. Sample use of a fixture

1 2 3 4 5 6 7 8 9TEST_F (myTestFixture1, UnitTest1) { . } TEST_F (myTestFixture1, UnitTest2) { . }

There are a few things that you need to understand when using fixtures:

You can do initialization or allocation of resources in either the constructor or theSetUpmethod. The choice is left to you, the user.

You can do deallocation of resources inTearDownor the destructor routine. However, if you want exception handling you must do it only in theTearDowncode because throwing an exception from the destructor results in undefined behavior.

The Google assertion macros may throw exceptions in platforms where they are enabled in future releases. Therefore, it's a good idea to use assertion macros in theTearDowncode for better maintenance.

The same test fixture isnotused across multiple tests. For every new unit test, the framework creates a new test fixture. So inListing 14, theSetUp(please use proper spelling here) routine is called twice because twomyFixture1objects are created.

Conclusion

This article just scratches the surface of the Google C++ Testing Framework. Detailed documentation about the framework is available from the Google site. For advanced developers, I recommend you read some of the other articles about open regression frameworks such as the Boost unit test framework and CppUnit.

Downloadable resources

PDF

Related topics

Google TestPrimer

Google TestAdvancedGuide

Google TestFAQ

Open source C/C++ unit testing tools, Part 1: Get to know the Boost unit test framework

What Every Computer Scientist Should Know About Floating-Point Arithmetic

Tags:AQ QU UI IC 
作者:网络 来源:yellow_hil