--- name: tdd-test-refactoring user-invocable: true description: "Use during Green/Refactor phase to eliminate test duplication. Extracts shared setup into TEST_F fixtures, removes redundant expectations, and keeps tests minimal and focused. Run after tests pass but before committing." --- # TDD Test Refactoring Patterns This skill complements **tdd-minimalism** by focusing on **test code quality** during the Green and Refactor phases. After implementation is minimal and passing, apply these patterns to keep the test suite clean. ## Principle Tests are code too — they accumulate duplication and cruft just like implementation. Each test should test **one distinct scenario** without repeating setup or expectations already covered by other tests. ## Refactoring Checklist After tests pass (Green phase), review the test file for these patterns: ### 1. Extract Shared Setup into TEST_F If multiple tests construct the same object(s) with the same configuration, extract the common setup into a fixture. **Before:** ```cpp TEST(VendingMachineTest, configured_machine_delivers_can) { VendingMachine machine; machine.Configure(Choice::Cola, Can::Coke); EXPECT_EQ(machine.Deliver(Choice::Cola), Can::Coke); } TEST(VendingMachineTest, multiple_choices_deliver_different_cans) { VendingMachine machine; machine.Configure(Choice::Cola, Can::Coke); machine.Configure(Choice::Fanta, Can::Orange); EXPECT_EQ(machine.Deliver(Choice::Cola), Can::Coke); EXPECT_EQ(machine.Deliver(Choice::Fanta), Can::Orange); } ``` **After:** ```cpp class VendingMachineTest : public ::testing::Test { protected: void SetUp() override { machine.Configure(Choice::Cola, Can::Coke); } VendingMachine machine; }; TEST_F(VendingMachineTest, configured_machine_delivers_can) { EXPECT_EQ(machine.Deliver(Choice::Cola), Can::Coke); } TEST_F(VendingMachineTest, multiple_choices_deliver_different_cans) { machine.Configure(Choice::Fanta, Can::Orange); EXPECT_EQ(machine.Deliver(Choice::Cola), Can::Coke); EXPECT_EQ(machine.Deliver(Choice::Fanta), Can::Orange); } ``` **When to keep TEST instead of TEST_F:** - The test needs a **different** or **empty** initial state (e.g., an unconfigured machine) - The test's setup is entirely unique and not shared with any other test ### 2. Remove Redundant Expectations If a test is a **strict superset** of another test (covers the same scenario plus more), the subset test may be redundant. **Check:** Does the superset test already assert everything the subset test asserts? | Scenario | Keep? | |----------|-------| | Superset test covers all assertions of subset | Consider removing subset if it adds no unique coverage | | Superset test covers subset's assertions **plus** additional ones | Keep both if subset documents a specific edge case clearly | | Tests cover different branches of the same logic | Keep both — they test different paths | ### 3. Name Tests by Scenario, Not by Order Test names should describe **what scenario** they cover, not their sequence number. | ❌ Bad | ✅ Good | |--------|--------| | `test1`, `test2`, `test3` | `choiceless_machine_deliver_nothing` | | `first_test`, `second_test` | `configured_machine_delivers_can` | | `test_feature_x` | `multiple_choices_deliver_different_cans` | ### 4. One Logical Assertion Per Test Each test should verify **one behavior**. Avoid asserting unrelated things in the same test. **When multiple assertions are OK:** - They verify the **same result** from different angles (e.g., `.has_value()` and `== expected`) - They verify a **post-condition** that naturally produces multiple observable effects **When to split:** - Assertions exercise **different code paths** or **different methods** - One assertion failing hides whether the other would also fail ### 5. Keep Tests Independent Tests must not depend on each other's side effects. Each test (or each fixture instance) starts fresh. - ✅ Each `TEST_F` gets a **fresh** `SetUp()` - ✅ No shared mutable state between tests - ❌ No test ordering dependencies ## Post-Refactoring Verification After applying test refactoring: - [ ] Run the test suite — all tests still pass - [ ] Run clang-format — all files comply - [ ] No test is a complete subset of another (unless documenting a specific edge case) - [ ] No duplicate setup code across tests using the same initial state - [ ] Test names describe the scenario, not the sequence ## Relationship to tdd-minimalism | Skill | Focus | When | |-------|-------|------| | tdd-minimalism | Implementation code — eliminate overengineering | Before writing implementation | | tdd-test-refactoring | Test code — eliminate duplication and cruft | After tests pass, before commit | Run **tdd-minimalism** before implementation. Run **tdd-test-refactoring** after tests pass but before committing.