Process
Read the source code. Load every function and class under test. Do not write tests from a summary or description — you must see the implementation.
Identify the test framework. Check for:
conftest.pyorpytest.ini→ pytestvitest.config.*→ Vitestjest.config.*→ Jest If none are found, default to pytest for Python, Vitest for TypeScript/JavaScript.
Categorize test types needed:
- Happy path: Normal inputs, expected outputs
- Edge cases: Empty inputs, None/null, boundary values, maximum lengths
- Error cases: Invalid inputs that should raise exceptions or return errors
- Integration: Tests that verify multiple components work together
Write tests following the AAA pattern:
def test_descriptive_name(): # Arrange — set up inputs and expected state user = create_test_user(name="Alice") # Act — call the function under test result = get_user_greeting(user) # Assert — verify the output assert result == "Hello, Alice!"Name tests descriptively. The test name should describe the scenario and expected outcome:
- ✅
test_login_rejects_empty_password - ❌
test_login_2
- ✅
Run the tests. Execute the full test suite and confirm all tests pass. If any fail, fix the test (not the source code) unless you discover an actual bug.
Rationalizations
| Excuse | Rebuttal |
|---|---|
| "I'll just write happy path tests" | Edge cases are where bugs live. Write at least one edge case per function. |
| "The function is too simple to test" | Simple functions get refactored. Tests protect against regressions during refactoring. |
| "I'll add tests later" | You won't. Write them now. |
| "Mocking everything is fine" | Over-mocking hides integration bugs. Mock external services, not your own code. |
Verification
- Tests were executed (not just written)
- All tests pass
- At least one edge case test exists per function
- Test names describe the scenario, not just "test_1", "test_2"