Effective C Testing Using Google Test
Effective C Testing Using Google Test
Road map
Why tests? Introduction to Google Test What to test for, and how How to make your code testable How to write good tests Summary
SLIDE 2
Why tests?
Software evolves.
Must not break existing behaviors. Must not introduce bugs in new behaviors. Unlikely to have complete knowledge on the code.
Writing automated tests is the only way in which development can scale.
SLIDE 3
You really cannot afford not writing tests: (Making large changes without tests) is like doing aerial gymnastics without a net. - Michael Feathers, unit tester and author of Working Effectively with Legacy Code Bugs detected later in the development process cost orders of magnitudes more to fix. They reflect in your paycheck!
SLIDE 4
SLIDE 4
SLIDE 5
SLIDE 6
SLIDE 7
Test
Test
Test
Each test is implemented as a function, using the TEST() or TEST_F() macro. SLIDE 9
Simple tests
Simple things are easy:
TEST() remembers the tests defined, so you dont have
// TEST(TestCaseName, TestName)
// Verifies the result of the function to be tested. EXPECT_EQ(0, p.Parse("0")); EXPECT_EQ(5, p.Parse("101")); }
SLIDE 10
Google Test creates a fresh object for each test tests wont affect each other!
SLIDE 11
SLIDE 12
Edge cases
EXPECT_TRUE(IsSubStringOf("", ""))
Bad input leads to: Expected error code easy Process crash Yes, you should test this!
Continuing in erroneous state is bad.
But how?
SLIDE 13
Death Tests
TEST(FooDeathTest, SendMessageDiesOnInvalidPort) { Foo a; a.Init(); EXPECT_DEATH(a.SendMessage(56, "test"), "Invalid port number"); }
How it works The statement runs in a forked sub-process. Very fast on Linux Caveat: side effects are in the sub-process too!
SLIDE 14
SLIDE 16
Dependency injection
Concerns: A little overhead, but often acceptable dont optimize prematurely. Inconvenient when constructing objects the factory pattern to the rescue. Other ways to break dependencies: Setters
void A::set_b(BInterface* b) { this->b_ = b; }
Template parameters
template<typename BType> class A { BType b_; }; A<B> obj;
SLIDE 18
SLIDE 19
SLIDE 20
SLIDE 21
EXPECT vs ASSERT
Two sets of assertions with same interface EXPECT (continue-after-failure) vs ASSERT (fail-fast) Prefer EXPECT: Reveals more failures. Allows more to be fixed in a single edit-compile-run cycle. Use ASSERT when it doesnt make sense to continue (seg fault, trash results). Example:
TEST(DataFileTest, HasRightContent) { ASSERT_TRUE(fp = fopen(path, "r")) << "Failed to open the data file.";
ASSERT_EQ(10, fread(buffer, 1, 10, fp)) << "The data file is smaller than expected."; EXPECT_STREQ("123456789", buffer) << "The data file is corrupted."; ... }
SLIDE 22
Not just any tests must cover the change No test, no check-in. Test only the delta. Resist the temptation for exceptions.
Over time, bring more code under test.
When adding to module Foo, might as well add tests for other parts of Foo.
Refactor the code along the way. It will not happen over night, but you can do it.
SLIDE 23
Resources
Learn Google Test: Homepage: http://code.google.com/p/googletest/ Primer: http://code.google. com/p/googletest/wiki/GoogleTestPrimer Questions: [email protected] Dependency injection, mocks Google C++ Mocking Framework (aka Google Mock): http://code.google.com/p/googlemock/
SLIDE 25
Summary
Key points to take home: 1. Keep tests small and obvious. 2. Test a module in isolation. 3. Break dependencies in production code. 4. Test everything that can possibly break, but no more. 5. No test, no check-in.
SLIDE 26