[2026] Google Test (gtest) for C++: From Setup to TEST, Fixtures, and CI
이 글의 핵심
Complete C++ unit testing guide with Google Test: setup, assertions, fixtures, parameterized tests, death tests, TDD workflow, CI integration, and production patterns with real-world examples.
Introduction: “I refactored and something else broke”
Without tests, refactors hide regressions until production. Unit tests verify functions and classes against expected outputs so CI can catch breaks on every commit.
If calculate changes from a + b to a * b by mistake, EXPECT_EQ(calculate(2, 3), 5) fails immediately with a clear message.
Environment: Google Test via FetchContent, vcpkg (vcpkg install gtest), or Conan. g++/Clang C++14+; on Windows, MSVC + vcpkg is a common combo.
Link gtest_main so you do not write main; or link gtest and provide RUN_ALL_TESTS() yourself.
Table of contents
- Setup with CMake
- Basic assertions
- Test fixtures
- Parameterized tests
- Death tests
- Real-world examples
- TDD workflow
- Common errors
- Debugging tips
- Best practices
- CI integration
- Production patterns
1. Setup with CMake
Method 1: FetchContent (recommended)
다음은 cmake를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
cmake_minimum_required(VERSION 3.14)
project(MyProject)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.12.1
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
enable_testing()
add_executable(myapp_test
test_main.cpp
test_calculator.cpp
)
target_link_libraries(myapp_test PRIVATE
gtest_main
myapp_lib # Your library under test
)
include(GoogleTest)
gtest_discover_tests(myapp_test)
Method 2: vcpkg
vcpkg install gtest
아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
find_package(GTest CONFIG REQUIRED)
add_executable(myapp_test test_main.cpp)
target_link_libraries(myapp_test PRIVATE
GTest::gtest_main
myapp_lib
)
Method 3: Conan
아래 코드는 ini를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# conanfile.txt
[requires]
gtest/1.12.1
[generators]
CMakeDeps
CMakeToolchain
find_package(GTest REQUIRED)
target_link_libraries(myapp_test PRIVATE GTest::gtest_main)
2. Basic assertions
EXPECT vs ASSERT
아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <gtest/gtest.h>
TEST(CalculatorTest, Addition) {
// EXPECT: continues after failure
EXPECT_EQ(2 + 2, 4);
EXPECT_EQ(3 + 3, 6); // Still runs even if above fails
// ASSERT: stops test on failure
int* ptr = new int(42);
ASSERT_NE(ptr, nullptr); // Must pass before using ptr
EXPECT_EQ(*ptr, 42); // Only runs if ptr is valid
delete ptr;
}
Common assertions
다음은 cpp를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Boolean
EXPECT_TRUE(condition);
EXPECT_FALSE(condition);
// Comparison
EXPECT_EQ(a, b); // ==
EXPECT_NE(a, b); // !=
EXPECT_LT(a, b); // <
EXPECT_LE(a, b); // <=
EXPECT_GT(a, b); // >
EXPECT_GE(a, b); // >=
// Strings
EXPECT_STREQ("hello", str); // C-strings
EXPECT_STRNE("world", str);
std::string s = "test";
EXPECT_EQ("test", s); // std::string works with EQ
// Floating point
EXPECT_FLOAT_EQ(1.0f, result);
EXPECT_DOUBLE_EQ(1.0, result);
EXPECT_NEAR(1.0, result, 0.001); // Within tolerance
// Exceptions
EXPECT_THROW(func(), std::runtime_error);
EXPECT_NO_THROW(func());
EXPECT_ANY_THROW(func());
Custom messages
EXPECT_EQ(expected, actual) << "Failed with input: " << input;
3. Test fixtures (TEST_F)
Basic fixture
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class DatabaseTest : public ::testing::Test {
protected:
Database* db;
// Runs before each test
void SetUp() override {
db = new Database("test.db");
db->connect();
}
// Runs after each test
void TearDown() override {
db->disconnect();
delete db;
}
};
TEST_F(DatabaseTest, InsertRecord) {
EXPECT_TRUE(db->insert("key", "value"));
EXPECT_EQ(db->get("key"), "value");
}
TEST_F(DatabaseTest, DeleteRecord) {
db->insert("key", "value");
EXPECT_TRUE(db->remove("key"));
EXPECT_EQ(db->get("key"), "");
}
Suite-level setup (shared across tests)
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class ExpensiveResourceTest : public ::testing::Test {
protected:
static Database* shared_db;
// Runs once before all tests in suite
static void SetUpTestSuite() {
shared_db = new Database("shared.db");
shared_db->connect();
}
// Runs once after all tests in suite
static void TearDownTestSuite() {
shared_db->disconnect();
delete shared_db;
shared_db = nullptr;
}
};
Database* ExpensiveResourceTest::shared_db = nullptr;
TEST_F(ExpensiveResourceTest, Test1) {
EXPECT_NE(shared_db, nullptr);
}
TEST_F(ExpensiveResourceTest, Test2) {
EXPECT_NE(shared_db, nullptr);
}
4. Parameterized tests
Basic parameterized test
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class SquareTest : public ::testing::TestWithParam<std::pair<int, int>> {};
TEST_P(SquareTest, CheckSquare) {
auto [input, expected] = GetParam();
EXPECT_EQ(input * input, expected);
}
INSTANTIATE_TEST_SUITE_P(
SquareValues,
SquareTest,
::testing::Values(
std::make_pair(0, 0),
std::make_pair(1, 1),
std::make_pair(2, 4),
std::make_pair(3, 9),
std::make_pair(-2, 4)
)
);
Multiple parameters with Combine
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class StringTest : public ::testing::TestWithParam<std::tuple<std::string, char, int>> {};
TEST_P(StringTest, CountChar) {
auto [str, ch, expected] = GetParam();
int count = std::count(str.begin(), str.end(), ch);
EXPECT_EQ(count, expected);
}
INSTANTIATE_TEST_SUITE_P(
StringCases,
StringTest,
::testing::Combine(
::testing::Values("hello", "world", "test"),
::testing::Values('l', 'o', 't'),
::testing::Range(0, 3)
)
);
Range and ValuesIn
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Range: [0, 10) step 2
INSTANTIATE_TEST_SUITE_P(RangeTest, MyTest, ::testing::Range(0, 10, 2));
// ValuesIn: from container
std::vector<int> inputs = {1, 2, 3, 5, 8, 13};
INSTANTIATE_TEST_SUITE_P(FibTest, MyTest, ::testing::ValuesIn(inputs));
5. Death tests
Basic death test
다음은 cpp를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
void DivideByZero(int a, int b) {
assert(b != 0 && "Division by zero");
return a / b;
}
TEST(MathDeathTest, DivisionByZero) {
EXPECT_DEATH(DivideByZero(10, 0), "Division by zero");
}
TEST(MathDeathTest, AbortOnError) {
ASSERT_DEATH({
if (condition) {
std::abort();
}
}, ".*"); // Regex for expected stderr
}
Platform-specific death tests
아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
TEST(DeathTest, CrossPlatform) {
#ifdef NDEBUG
EXPECT_DEATH_IF_SUPPORTED(func(), "error");
#else
EXPECT_DEATH(func(), "error");
#endif
}
6. Real-world examples
Example 1: String utility class
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class StringUtils {
public:
static std::string trim(const std::string& str) {
size_t start = str.find_first_not_of(" \t\n\r");
if (start == std::string::npos) return "";
size_t end = str.find_last_not_of(" \t\n\r");
return str.substr(start, end - start + 1);
}
static std::vector<std::string> split(const std::string& str, char delim) {
std::vector<std::string> result;
std::stringstream ss(str);
std::string item;
while (std::getline(ss, item, delim)) {
result.push_back(item);
}
return result;
}
};
TEST(StringUtilsTest, TrimWhitespace) {
EXPECT_EQ(StringUtils::trim(" hello "), "hello");
EXPECT_EQ(StringUtils::trim("\t\ntest\r\n"), "test");
EXPECT_EQ(StringUtils::trim(""), "");
EXPECT_EQ(StringUtils::trim(" "), "");
}
TEST(StringUtilsTest, SplitString) {
auto result = StringUtils::split("a,b,c", ',');
ASSERT_EQ(result.size(), 3);
EXPECT_EQ(result[0], "a");
EXPECT_EQ(result[1], "b");
EXPECT_EQ(result[2], "c");
}
Example 2: HTTP client with mock
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class HttpClient {
public:
virtual ~HttpClient() = default;
virtual std::string get(const std::string& url) = 0;
};
class RealHttpClient : public HttpClient {
public:
std::string get(const std::string& url) override {
// Actual HTTP implementation
return "real response";
}
};
class MockHttpClient : public HttpClient {
public:
std::string get(const std::string& url) override {
return "mock response";
}
};
class ApiService {
HttpClient* client;
public:
explicit ApiService(HttpClient* c) : client(c) {}
std::string fetchData(const std::string& endpoint) {
return client->get("https://api.example.com/" + endpoint);
}
};
TEST(ApiServiceTest, FetchData) {
MockHttpClient mock;
ApiService service(&mock);
std::string result = service.fetchData("users");
EXPECT_EQ(result, "mock response");
}
Example 3: Thread-safe queue
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
template<typename T>
class ThreadSafeQueue {
std::queue<T> queue;
mutable std::mutex mutex;
public:
void push(T value) {
std::lock_guard<std::mutex> lock(mutex);
queue.push(std::move(value));
}
bool try_pop(T& value) {
std::lock_guard<std::mutex> lock(mutex);
if (queue.empty()) return false;
value = std::move(queue.front());
queue.pop();
return true;
}
size_t size() const {
std::lock_guard<std::mutex> lock(mutex);
return queue.size();
}
};
TEST(ThreadSafeQueueTest, PushAndPop) {
ThreadSafeQueue<int> q;
q.push(1);
q.push(2);
int value;
ASSERT_TRUE(q.try_pop(value));
EXPECT_EQ(value, 1);
ASSERT_TRUE(q.try_pop(value));
EXPECT_EQ(value, 2);
EXPECT_FALSE(q.try_pop(value));
}
TEST(ThreadSafeQueueTest, Concurrent) {
ThreadSafeQueue<int> q;
const int N = 1000;
std::thread producer([&]() {
for (int i = 0; i < N; ++i) {
q.push(i);
}
});
std::thread consumer([&]() {
int count = 0;
int value;
while (count < N) {
if (q.try_pop(value)) {
++count;
}
}
});
producer.join();
consumer.join();
EXPECT_EQ(q.size(), 0);
}
7. TDD workflow
Red-Green-Refactor cycle
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 1. RED: Write failing test
TEST(CalculatorTest, Multiply) {
Calculator calc;
EXPECT_EQ(calc.multiply(2, 3), 6); // Fails: multiply not implemented
}
// 2. GREEN: Minimal implementation
class Calculator {
public:
int multiply(int a, int b) {
return a * b; // Simplest solution
}
};
// 3. REFACTOR: Improve without breaking test
class Calculator {
public:
int multiply(int a, int b) {
// Add validation, logging, etc.
if (a == 0 || b == 0) return 0;
return a * b;
}
};
Test-first development
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Step 1: Write test for new feature
TEST(UserServiceTest, CreateUser) {
UserService service;
User user = service.createUser("john", "john@example.com");
EXPECT_EQ(user.name, "john");
EXPECT_EQ(user.email, "john@example.com");
EXPECT_FALSE(user.id.empty());
}
// Step 2: Implement to pass test
class UserService {
public:
User createUser(const std::string& name, const std::string& email) {
User user;
user.id = generateId();
user.name = name;
user.email = email;
return user;
}
};
8. Common errors and fixes
Error 1: Undefined reference to InitGoogleTest
undefined reference to `testing::InitGoogleTest(int*, char**)'
Fix: Link gtest_main or provide main
target_link_libraries(myapp_test PRIVATE gtest_main)
Error 2: Multiple definitions of main
multiple definition of `main'
Fix: Don’t define main when using gtest_main 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Remove this if using gtest_main
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Error 3: pthread error on Linux
undefined reference to `pthread_create'
Fix: Link Threads
find_package(Threads REQUIRED)
target_link_libraries(myapp_test PRIVATE gtest_main Threads::Threads)
Error 4: Death test fails in Release
Expected: DivideByZero(10, 0) dies
Actual: it didn't die
Fix: assert() is disabled in Release (NDEBUG) 아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Use custom assertion that works in Release
#define MY_ASSERT(cond, msg) \
if (!(cond)) { \
std::cerr << msg << std::endl; \
std::abort(); \
}
Error 5: Flaky tests in CI
Test passed locally but fails randomly in CI
Fix: Remove nondeterminism 아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// BAD: depends on timing
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// GOOD: use synchronization
std::condition_variable cv;
std::mutex m;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, []{ return ready; });
9. Debugging tips
Tip 1: Run specific tests
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# Run all tests in suite
./myapp_test --gtest_filter=CalculatorTest.*
# Run specific test
./myapp_test --gtest_filter=CalculatorTest.Addition
# Exclude tests
./myapp_test --gtest_filter=-*Slow*
Tip 2: Repeat tests
아래 코드는 bash를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# Repeat 100 times to catch flaky tests
./myapp_test --gtest_repeat=100
# Break on first failure
./myapp_test --gtest_repeat=100 --gtest_break_on_failure
Tip 3: Verbose output
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# Show all test names
./myapp_test --gtest_list_tests
# Colorized output
./myapp_test --gtest_color=yes
Tip 4: SCOPED_TRACE for loops
아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
TEST(LoopTest, MultipleValues) {
std::vector<int> inputs = {1, 2, 3, 4, 5};
for (size_t i = 0; i < inputs.size(); ++i) {
SCOPED_TRACE("Testing input at index " + std::to_string(i));
EXPECT_GT(inputs[i], 0);
}
}
Tip 5: Debug with GDB
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# Build with debug symbols
cmake -DCMAKE_BUILD_TYPE=Debug ..
# Run under GDB
gdb --args ./myapp_test --gtest_filter=MyTest.Specific
(gdb) break MyTest_Specific_Test::TestBody
(gdb) run
10. Best practices
1. One assertion per test (when possible)
다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// BAD: Multiple unrelated assertions
TEST(UserTest, Everything) {
User user("john");
EXPECT_EQ(user.getName(), "john");
EXPECT_TRUE(user.isValid());
EXPECT_EQ(user.getAge(), 0);
}
// GOOD: Separate tests
TEST(UserTest, GetName) {
User user("john");
EXPECT_EQ(user.getName(), "john");
}
TEST(UserTest, IsValid) {
User user("john");
EXPECT_TRUE(user.isValid());
}
2. Descriptive test names
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// BAD
TEST(UserTest, Test1) { }
// GOOD
TEST(UserTest, GetName_WithValidInput_ReturnsName) { }
TEST(UserTest, IsValid_WithEmptyName_ReturnsFalse) { }
3. AAA pattern (Arrange-Act-Assert)
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
TEST(CalculatorTest, Add_TwoPositiveNumbers_ReturnsSum) {
// Arrange
Calculator calc;
int a = 2, b = 3;
// Act
int result = calc.add(a, b);
// Assert
EXPECT_EQ(result, 5);
}
4. Test edge cases
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
TEST(StringTest, EdgeCases) {
EXPECT_EQ(trim(""), ""); // Empty
EXPECT_EQ(trim(" "), ""); // Only whitespace
EXPECT_EQ(trim("a"), "a"); // Single char
EXPECT_EQ(split("", ',').size(), 1); // Empty split
}
5. Avoid test interdependence
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// BAD: Tests depend on order
static int counter = 0;
TEST(BadTest, First) { counter = 1; }
TEST(BadTest, Second) { EXPECT_EQ(counter, 1); } // Fragile!
// GOOD: Each test is independent
TEST(GoodTest, First) {
int counter = 0;
counter = 1;
EXPECT_EQ(counter, 1);
}
11. CI integration
GitHub Actions
다음은 yaml를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: sudo apt-get install -y cmake g++
- name: Build
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
- name: Run tests
run: |
cd build
ctest --output-on-failure
- name: Generate coverage
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON
cmake --build build
cd build
ctest
lcov --capture --directory . --output-file coverage.info
lcov --remove coverage.info '/usr/*' --output-file coverage.info
lcov --list coverage.info
CTest integration
아래 코드는 cmake를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
enable_testing()
add_executable(myapp_test test_main.cpp)
target_link_libraries(myapp_test PRIVATE gtest_main myapp_lib)
include(GoogleTest)
gtest_discover_tests(myapp_test)
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# Run all tests
ctest
# Verbose output
ctest -V
# Run specific test
ctest -R CalculatorTest
# Parallel execution
ctest -j4
12. Production patterns
Pattern 1: Separate test directory
아래 코드는 code를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
project/
├── CMakeLists.txt
├── src/
│ ├── calculator.h
│ └── calculator.cpp
└── test/
├── CMakeLists.txt
└── test_calculator.cpp
Pattern 2: Test utilities
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// test/test_utils.h
namespace test_utils {
inline std::string readFile(const std::string& path) {
std::ifstream file(path);
return std::string((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
}
inline void createTempFile(const std::string& path, const std::string& content) {
std::ofstream file(path);
file << content;
}
}
Pattern 3: Custom matchers
아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
testing::AssertionResult IsEven(int n) {
if (n % 2 == 0)
return testing::AssertionSuccess();
else
return testing::AssertionFailure() << n << " is odd";
}
TEST(NumberTest, EvenNumbers) {
EXPECT_TRUE(IsEven(2));
EXPECT_TRUE(IsEven(4));
}
Summary
- Setup: FetchContent, vcpkg, or Conan
- Assertions: EXPECT_* (continue) vs ASSERT_* (stop)
- Fixtures: TEST_F for setup/teardown
- Parameterized: TEST_P for data-driven tests
- Death tests: EXPECT_DEATH for abort/exit verification
- TDD: Red-Green-Refactor cycle
- CI: ctest integration with GitHub Actions
- Best practices: One assertion per test, descriptive names, AAA pattern
Next: Google Mock (#18-2)
Previous: Package managers (#17-2)
Keywords
Google Test, gtest, C++ unit testing, TDD, CTest, test fixtures, parameterized tests