5 properties of Good Tests ========================== - Correctness - Readability - Completeness - Demonstrability - Resilience Correctness - Write correct tests Readablity - Yourself and others - Perhaps a documentation Completeness - Test the full of my API - Only my API Demonstrability - Show use cases Resilience - Only failes when we want to it - Never fails otherwise Correctness =========== Tests depend upon known bugs int square( int x) { // TODO: Implement return 0; } TEST (SquareTest, MathTests) { EXPECT_EQ( 0, square(2)); EXPECT_EQ( 0, square(3)); EXPECT_EQ( 0, square(5)); } - code review! TEST (SquareTest, MathTests) { EXPECT_EQ( 4, square(2)); EXPECT_EQ( 9, square(3)); EXPECT_EQ( 25, square(5)); } If the test fails, we should decide who to blame: - the writer of the code?, or - the writer of the tests? - Log library issue Tests that don't execute real scenarios class MockWorld : public World { // assume world is flat bool IsFlat() override { return true; } }; TEST (Flat, WorldTests) { MockWorld world; EXPECT_TRUE( world.Populate() ); EXPECT_TRUE( world.IsFlat() ); } - Testing the Mock not the real code Readability =========== Tests should be obvious tothe future reader (including yourself) Mistakes: - Too much boilerplate and other distraction TEST (BigSystemTest, CallIsUnimplemented) { TestStorageSystem storage; auto test_data = GetTestFileMap(); storage.MapFilesystem(test_data); BigSystem system; ASSERT_OK( system.Initialize(5)); ThreadPoool pool(10); pool.StartThreads(); storage.SetThreads(pool); system.SetStorage(storage); ASSERT_TRUE(system.IsRunning()); EXPECT_TRUE( IsUnimplemented(system.Status()) ); // actual test } - Not enough context in the test (opposite than previos) (hiding important details) TEST (BigSystemTest, ReadMagicBytes) { BigSystem system = InitializeTestSystemAndTestData(); EXPECT_EQ( 42, system.PrivateKey() ); } At least comment it! - Don't use advanced test framework features when it is not necessary class BigSystemTest : public ::testing::Test { public: BigSystemTest() : filename_("/tmp/test") { } void SetUp() { ASSERT_OK( file::WriteData(filename_, "Hello world!\n") ); } protected: BigSystem system_; std::string filename_; }; TEST_F(BigSystemTest, BasicTest) { EXPECT_TRUE(system_.Initialize()); } is equivalent with that: TEST_F(BigSystemTest, BasicTest) { BigSystem system; EXPECT_TRUE(system.Initialize()); } is equivalent? Perhaps there are hidden activities when writing file::WriteData() ... A test is like a novel: - setup - action - conclusion Completeness ============ - test for the easy cases: TEST (FactorialTest, basicTests) { EXPECT_EQ( 1, Factorial(1) ); EXPECT_EQ( 120, Factorial(5) ); } int Factorial( int n) { if ( 1 == n ) return 1; if ( 5 == n ) return 120; return -1; } - test for the edge cases TEST (FactorialTest, basicTests) { EXPECT_EQ( 1, Factorial(1) ); EXPECT_EQ( 120, Factorial(5) ); EXPECT_EQ( 1, Factorial(0) ); EXPECT_EQ( 479001600, Factorial(12) ); EXPECT_EQ( std::numeric_limits::max<int>(), Factorial(13) ); // overflow EXPECT_EQ( 1, Factorial(0) ); // check, no internal state EXPECT_EQ( std::numeric_limits::max<int>(), Factorial(-TEST ( Foo, First) { ASSERT_EQ( 0, i); ++i; } 10) ); // overflow } perhaps write tests first: not be driven by the implementation... - test only what we are responsible for TEST (FilterTest, WithVector) { vector<int> v; // make sure vector is working v.push_back(1); EXPECT_EQ( 1, v.size() ); v.clear(); EXPECT_EQ( 0, v.size() ); EXPECT_TRUE( v.empty() ); // Now test our stuff v = Filter( { 1,2,3,4,5}, [](int x) { 0 == return x % 2; } ); EXPECT_THAT( v, ElementsAre(2,4) ); } Where to draw the line? I should test only what I implemented. TEST (FilterTest, WithVector) { vector<int> v; v = Filter( { 1,2,3,4,5}, [](int x) { 0 == return x % 2; } ); EXPECT_THAT( v, ElementsAre(2,4) ); } Demonstrabilty ============== Clients will learn the system via tests. Tests should serve as a demonstration of how teh API works. - Using private API is bad. - Using friend + test only methods are bad. - Bad usage in unit tests suggesting a bad API class Foo { friend FooTest; public: bool Setup(); private: bool ShortcutSetupForTesting(); }; TEST (FooTest, Setup) { EXPECT_TRUE( ShortcutSetupForTesting() ); } No user can call ShortcutSetupForTesting(). class Foo { public: bool Setup(); }; TEST (FooTest, Setup) { EXPECT_TRUE( Setup() ); } Resilience ========== - Write tests that depend only on published API guarantees! Mistakes: - Flaky tests (re-run gets different results) - Brittle tests (depending on too many assumptions, implementation details) - Tests depending on execution order - Mocks depending upon underlying APIs - Non-hermetic tests Flaky test ---------- TEST ( UpdaterTest, RunsFast) { Updater updater; updater.UpdateAsync(); SleepFor(Seconds(.5)); // should be enough EXPECT_TRUE( updater.Updated() ); } - RotatingLogFileTest at midnight ... Brittle test ------------ Tests that can fail for changes unrelated to the code under test. Might be our code changes, but not **this** part. TEST ( Tags, ContentsAreCorrect) { TagSet tags = {5,8,10}; // unordered set EXPECT_THAT( tags, ElementsAre(8,5,10) ); } Correct: TEST ( Tags, ContentsAreCorrect) { TagSet tags = {5,8,10}; // unordered set EXPECT_THAT( tags, UnorderedElementsAre(5,8,10) ); } What about that? TEST ( MyTest, LogWasCalled) { StartLogCapture(); EXPECT_TRUE( Frobber::start() ); EXPECT_TRUE( Logs(), Contains("file.cc:421: OpenedFile frobber.config") ); } Can fail for changes unrelated to the code under test. Red flag: "Run your code twice". Perhaps use here regular expression... and set up boundaries wher my log should be. Ordering -------- Tests fail if they aren't run all together or in a particular order. static int i = 0; TEST ( Foo, First) { ASSERT_EQ( 0, i); ++i; } TEST ( Foo, Second) { ASSERT_EQ( 1, i); ++i; } Many test environments runs tests paralelry or somebody touch the state. Global state is bad idea, causing hidden dependency. Files, etc ... Hermetic -------- Test fails if anyone else runs the same test at the same time. TEST (Foo, StorageTest) { StorageServer *server = GetStorageServerHandle(); auto val = rand(); server->Store("testkey", val); EXPECT_EQ( val, server->Load("testkey") ); } - std::this_thread::get_id() - putenv() Deep dependency --------------- class File { public: ... virtual bool Stat( Stat *stat); virtual bool StatWithOptions( Stat *stat, Options, options) { return Stat(stat); // ignore options } }; TEST (MyTest, FSUsage) { ... EXPECT_CALL( file, Stat(_)).Times(1); Frobber::Stat(); } Will fail when StatWithOptions() is implemented directly, not via Stat(). People are depending on implicit interfaces (call order, etc...) Goals: ====== - Correctness: write test testing what you wanted to test - Readability : write readable tests, use code review - Completeness: test all edge cases, but test only what you are responsible for - Demonstrability: show how to use the API - Resilience: hermetic, only breaks when unacceptable behaviour change happens Goals are not RULES. Who writes the test? - implementer or somebody else What to do, when you have a large tests with complex state? How to test Async operations - separate goals: - does it give the correct results? - does it do fast? don't try it in one test - e.g. sanitizers etc. have overhead.