Google testing framework (gtest)
- Tutorial
When the question arose about testing code, I did not hesitate to use boost :: test. To expand my horizons I tried the Google Test Framework. In addition to all the goodies it contains, unlike boost :: test, the project is booming. I would like to share the acquired knowledge. I ask everyone who is interestedKey concepts
A key concept in the Google test framework is the concept of assert. An assertion is an expression that can result in success, nonfatal failure, and fatal failure. A critical failure causes the test to complete; in other cases, the test continues. The test itself is a collection of statements. In addition, tests can be grouped in sets (test case). If it is difficult custom group of objects to be used in the various tests, it is possible to use a fixation (fixture). The combined test suites are a test program .
Assertions
Statements that generate critical failures, if false, begin with ASSERT_, noncritical - EXPECT_. It should be borne in mind that in the event of a critical failure, an immediate return is made from the function in which the statement that caused the failure was encountered. If this statement is followed by some kind of memory-cleaning code or some other final procedures, you may get a memory leak.
The following statements are available (non-critical ones begin not with ASSERT_, but with EXPECT_):
Simple logic
- ASSERT_TRUE (condition);
- ASSERT_FALSE (condition);
Comparison
- ASSERT_EQ (expected, actual); - =
- ASSERT_NE (val1, val2); -! =
- ASSERT_LT (val1, val2); - <
- ASSERT_LE (val1, val2); - <=
- ASSERT_GT (val1, val2); ->
- ASSERT_GE (val1, val2); -> =
String comparison
- ASSERT_STREQ (expected_str, actual_str);
- ASSERT_STRNE (str1, str2);
- ASSERT_STRCASEEQ (expected_str, actual_str); - case insensitive
- ASSERT_STRCASENE (str1, str2); - case insensitive
Exception Check
- ASSERT_THROW (statement, exception_type);
- ASSERT_ANY_THROW (statement);
- ASSERT_NO_THROW (statement);
Predicate check
- ASSERT_PREDN (pred, val1, val2, ..., valN); - N <= 5
- ASSERT_PRED_FORMATN (pred_format, val1, val2, ..., valN); - works similar to the previous one, but allows you to control the output
Floating Point Comparison
- ASSERT_FLOAT_EQ (expected, actual); - inaccurate float comparison
- ASSERT_DOUBLE_EQ (expected, actual); - inaccurate double comparison
- ASSERT_NEAR (val1, val2, abs_error); - the difference between val1 and val2 does not exceed the error abs_error
Challenge failure or success
- SUCCEED ();
- FAIL ();
- ADD_FAILURE ();
- ADD_FAILURE_AT ("file_path", line_number);
You can write your own function that returns AssertionResult
::testing::AssertionResult IsTrue(bool foo)
{
if (foo)
return ::testing::AssertionSuccess();
else
return ::testing::AssertionFailure() << foo << " is not true";
}
TEST(MyFunCase, TestIsTrue)
{
EXPECT_TRUE(IsTrue(false));
}
You can control data types using the function :: testing :: StaticAssertTypeEq
If the statement is false, the data used in the statement is issued. You can also set your own comment:
ASSERT_EQ(1, 0) << "1 is not equal 0";
Extended character sets (wchar_t) can be used both in comments and in assertions regarding strings. In this case, the output will be in UTF-8 encoding.
Tests
To determine the test, use the TEST macro. It defines a void function in which statements can be used. As noted earlier, a critical failure causes an immediate return from the function.
TEST(test_case_name, test_name)
{
ASSERT_EQ(1, 0) << "1 is not equal 0";
}
TEST accepts 2 parameters that uniquely identify the test - the name of the test suite and the name of the test. Within the same test set, the test names must not match. If the name begins with DISABLED_, it means that you have marked the test (test suite) as temporarily unused.
You can use statements not only as part of the test, but also call them from any function. There is only one limitation - statements that generate critical failures cannot be called from non-void functions.
Fixtures
It happens that the objects involved in testing are difficult to configure for each test. You can set the setup process once and execute it for each test automatically. In such situations, fixations are used.
A commit is a class inherited from :: testing :: Test, inside of which all the objects necessary for testing are declared, while in the constructor or in the SetUp () function, they are set up, and resources are released in the TearDown () function. Tests themselves that use commits should be declared using the TEST_F macro, the first parameter of which should be specified not the name of the test suite, but the name of the commit.
A new commit will be created for each test, configured using SetUp (), the test will be launched, resources will be freed using TearDown (), and the commit object will be deleted. Thus, each test will have its own copy of the fixation “not corrupted” by the previous test.
#include
#include
class Foo
{
public:
Foo()
: i(0)
{
std::cout << "CONSTRUCTED" << std::endl;
}
~Foo()
{
std::cout << "DESTRUCTED" << std::endl;
}
int i;
};
class TestFoo : public ::testing::Test
{
protected:
void SetUp()
{
foo = new Foo;
foo->i = 5;
}
void TearDown()
{
delete foo;
}
Foo *foo;
};
TEST_F(TestFoo, test1)
{
ASSERT_EQ(foo->i, 5);
foo->i = 10;
}
TEST_F(TestFoo, test2)
{
ASSERT_EQ(foo->i, 5);
}
int main(int argc, char *argv[])
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
In some cases, creating test objects is a very expensive operation, and tests do not make any changes to the objects. In this case, you can not re-create the commit for each test, but use a distributed commit with global SetUp () and TearDown (). A commit automatically becomes distributed if the class has at least one static member. The static functions SetUpTestCase () and TearDownTestCase () will be called to configure the object and release resources, respectively. Thus, the test suite before the first test will call SetUpTestCase (), and after the last TearDownTestCase ().
If there is a need for SetUp () and TearDown () for the entire testing program, and not just for a test suite, you must create an inheritor class for :: testing :: Environment, override SetUp () and TearDown () and register it using the function AddGlobalTestEnvironment.
Running tests
Having declared all the necessary tests, we can run them using the RUN_ALL_TESTS () function. A function can only be called once. It is desirable that the test program returns the result of the function RUN_ALL_TESTS (), since some automatic testing tools determine the result of the test program by what it returns.
Flags
The InitGoogleTest (argc, argv) function called before RUN_ALL_TESTS () makes your test program more than just an executable file that displays the test results. This is a complete application that accepts input parameters that change its behavior. As usual, the -h, --help options will give you a list of all supported options. I will list some of them (for a complete list, see the documentation).
- ./test --gtest_filter = TestCaseName. * - TestCaseName.SomeTest - run all tests of the TestCaseName set except SomeTest
- ./test --gtest_repeat = 1000 --gtest_break_on_failure - start the testing program 1000 times and stop at the first failure
- ./test --gtest_output = "xml: out.xml" - in addition to the output in std :: out, out.xml will be created - an XML report with the results of the test program
- ./test --gtest_shuffle - run tests randomly
If you use any parameters constantly, you can set the appropriate environment variable and run the executable file without parameters. For example, setting the variable GTEST_ALSO_RUN_DISABLED_TESTS to a non-zero value is equivalent to using the --gtest_also_run_disabled_tests flag.
Instead of a conclusion
In this post, I briefly went over the main functions of the Google Test Framework. For more information, refer to the documentation . From there, you can get information about ASSERT_DEATH used when the program crashes, about maintaining additional logs, about parameterized tests, setting up output, testing private class members and much more.
UPD: According to the fair remark of the nikel user , a brief information on the use of flags has been added.
UPD 2: Correction of a marking after changes on Habré (native tag source).