← Back to Blog

Why Good Tests Matter

January 25, 2025

testingtest automationqualitybest practices

The Real Purpose of Good Tests: It's Multipurpose

Most people think tests exist to find bugs. While that's part of it, well-written tests serve multiple critical purposes that go far beyond defect detection.

The Multipurpose Nature of Good Tests

When tests are written correctly, they simultaneously:

  • Find bugs (the obvious one)
  • Document behavior (better than most documentation)
  • Enable refactoring (confidence to change code)
  • Improve design (hard-to-test code = poorly designed code)
  • Reduce risk (deploy with confidence)
  • Provide fast feedback (know immediately when something breaks)

The key phrase: "when written correctly." So what makes a test "good"?

The 12 Characteristics of Good Automated Tests

Based on industry best practices, good tests should be:

1. Automated

Run without human intervention. Click a button, get results. No manual steps, no manual verification.

2. Self-Evaluating

Tests check their own results. Pass or fail—no human needed to interpret output.

# Good: Self-evaluating
assert response.status_code == 200
assert response.data['user_id'] == expected_id

# Bad: Requires manual checking
print(response.data)  # Someone has to look at this

3. Repeatable

Run it 100 times, get the same result every time. No flakiness, no environmental dependencies.

4. Independent

Each test stands alone. Order doesn't matter. Tests don't depend on each other's data or state.

5. Fast

Unit tests in milliseconds, integration tests in seconds. Slow tests don't get run, and tests that don't run have zero value.

6. Isolated

Tests don't interfere with each other. No shared state. No race conditions.

7. Maintainable

Easy to update when requirements change. Clear, readable, well-organized code.

Example of maintainable test structure:

def test_user_checkout_flow():
    # Arrange
    user = create_test_user()
    product = add_product_to_cart(user)

    # Act
    result = checkout(user, product)

    # Assert
    assert result.success == True
    assert result.order_id is not None

8. Trustworthy

When a test fails, there's actually a problem. When it passes, the feature actually works. No false positives or negatives.

9. Focused

Test one thing at a time. When it fails, you know exactly what broke.

10. Separated

Test code is separate from production code. Tests shouldn't pollute your system with test-specific logic.

11. Deterministic

Given the same input, always produces the same output. No randomness, no "sometimes it fails."

12. Informative

When a test fails, the error message tells you what went wrong and where to look.

# Bad: Uninformative
assert user.is_valid()  # Why did this fail?

# Good: Informative
assert user.email is not None, f"User {user.id} missing email"
assert "@" in user.email, f"Invalid email format: {user.email}"

Why This Matters: The Multipurpose Test

Here's where it gets interesting. A test that has all 12 characteristics doesn't just find bugs—it serves multiple purposes simultaneously:

Purpose 1: Living Documentation

def test_refund_policy_full_refund_within_30_days():
    order = create_order(days_ago=15)
    refund = process_refund(order)
    assert refund.amount == order.total
    assert refund.fee == 0

This test documents the business rule: full refunds within 30 days, no fees. No need to dig through requirements docs—the test shows exactly how it works.

Purpose 2: Refactoring Safety Net

You want to optimize the checkout flow? With good tests, you refactor with confidence. Tests pass = behavior unchanged.

Purpose 3: Design Feedback

Struggling to write tests for a function? That's a code smell. Hard-to-test code is usually poorly designed code with too many dependencies or unclear responsibilities.

Purpose 4: Regression Prevention

That bug you fixed last month? The test you wrote ensures it never comes back.

Purpose 5: Continuous Deployment Enabler

You can't deploy 10 times a day without automated tests. Good tests make CI/CD possible.

The Cost of Bad Tests

Tests that violate these characteristics become:

  • ❌ Maintenance burdens (brittle, hard to update)
  • ❌ Time wasters (flaky, slow, unreliable)
  • ❌ Obstacles to deployment (can't trust results)
  • ❌ Sources of frustration (false failures)

Bad tests are worse than no tests because they create a false sense of security while slowing down development.

Practical Checklist

Before merging a new test, ask:

  • Does it run automatically in CI/CD?
  • Does it check its own results?
  • Can I run it 10 times and get the same result?
  • Does it run independently of other tests?
  • Does it complete in seconds (not minutes)?
  • Is the test code separate from production code?
  • If it fails, will I know exactly what broke?
  • Is the test name descriptive?

Conclusion

The purpose of tests isn't just finding bugs—it's enabling fast, confident software delivery.

When tests are:

  • Automated and self-evaluating
  • Fast and independent
  • Maintainable and trustworthy

They become multipurpose tools that document behavior, enable refactoring, reduce risk, and provide fast feedback.

Good tests don't just find problems—they prevent them, document solutions, and enable continuous improvement.

Write tests with all 12 characteristics, and you'll discover they serve far more purposes than you originally intended.


What characteristics do your tests have? Which ones are missing? Let's discuss on LinkedIn.