agentsclimarketplace

Test driven development

Skill furkangonel/cowrangler/bundled_skills/software-development/test-driven-development

Autonomous terminal AI agent for workflows and feasible project procedures. Co-Worker Co-Wrangler πŸ™

Install
npx -y skills add furkangonel/cowrangler --skill test-driven-development

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

TDD workflow β€” Red, Green, Refactor cycle with practical examples.

SKILL.md

10.1 KB, as published. Nobody here has run it

Test-Driven Development (TDD) SOP

Write a failing test, write just enough code to pass it, then refactor. Repeat. This SOP covers the full Red β†’ Green β†’ Refactor cycle with concrete examples.

When to Use

  • User wants to build a feature with test coverage from the start
  • User wants to practice TDD on an existing or new function
  • User is adding behavior to an existing module and wants to do it safely
  • User is fixing a bug and wants to prevent regression

The TDD Cycle

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                                                     β”‚
  β”‚   RED ──────→ GREEN ──────→ REFACTOR ──────→ RED   β”‚
  β”‚                                                     β”‚
  β”‚   Write a   Make it pass   Clean it up   Next test  β”‚
  β”‚   failing   with minimal   without        (repeat)   β”‚
  β”‚   test      code           breaking tests            β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Non-negotiable rules:

  1. Never write production code before a failing test
  2. Write only enough production code to make the failing test pass
  3. Never refactor on a red test β€” only refactor when all tests are green
  4. Run tests after every change, no matter how small

Step 1 β€” RED: Write a Failing Test

Test Anatomy (Arrange β†’ Act β†’ Assert)

def test_calculate_order_total_applies_discount():
    # Arrange β€” set up inputs
    items = [
        {"name": "Widget", "price": 10.00, "quantity": 3},
        {"name": "Gadget", "price": 25.00, "quantity": 1},
    ]
    discount_percent = 10

    # Act β€” call the code under test
    total = calculate_order_total(items, discount_percent)

    # Assert β€” verify the result
    assert total == 49.50  # (30 + 25) * 0.9

Good test names describe behavior, not implementation:

  • test_calculate_order_total_applies_discount β€” good
  • test_total_function β€” bad
  • test_with_10_percent_off_correctly_applies_the_discount_to_three_items β€” too verbose

Run It β€” Confirm It Fails

pytest tests/test_order.py::test_calculate_order_total_applies_discount -v
# Expected: FAILED (NameError or ImportError β€” function doesn't exist yet)

If the test passes immediately without writing production code, the test is wrong.


Step 2 β€” GREEN: Minimal Implementation

Write the simplest code that makes the test pass. Do not add features beyond what the test requires.

# order.py
def calculate_order_total(items, discount_percent=0):
    subtotal = sum(item["price"] * item["quantity"] for item in items)
    discount = subtotal * (discount_percent / 100)
    return round(subtotal - discount, 2)
pytest tests/test_order.py::test_calculate_order_total_applies_discount -v
# Expected: PASSED βœ“

Run the full test suite to ensure nothing regressed:

pytest tests/ -v
# All existing tests must still pass

Step 3 β€” REFACTOR: Clean Up

With green tests as a safety net, improve the code's structure without changing behavior.

# After refactor β€” same behavior, cleaner implementation
def calculate_order_total(items: list[dict], discount_percent: float = 0) -> float:
    """Calculate order total with optional percentage discount applied."""
    if not 0 <= discount_percent <= 100:
        raise ValueError(f"discount_percent must be 0-100, got {discount_percent}")

    subtotal = sum(item["price"] * item["quantity"] for item in items)
    return round(subtotal * (1 - discount_percent / 100), 2)
pytest tests/ -v
# All tests still green after refactor

Full TDD Walkthrough Example

Building a PasswordValidator class from scratch using TDD.

Iteration 1 β€” Minimum length

# tests/test_password_validator.py

from password_validator import PasswordValidator

def test_password_shorter_than_8_chars_is_invalid():
    validator = PasswordValidator()
    result = validator.validate("short")
    assert result.is_valid is False
    assert "at least 8 characters" in result.errors[0]
pytest -v  # RED: ImportError
# password_validator.py β€” minimal implementation
from dataclasses import dataclass, field

@dataclass
class ValidationResult:
    is_valid: bool
    errors: list[str] = field(default_factory=list)

class PasswordValidator:
    def validate(self, password: str) -> ValidationResult:
        errors = []
        if len(password) < 8:
            errors.append("Password must be at least 8 characters")
        return ValidationResult(is_valid=len(errors) == 0, errors=errors)
pytest -v  # GREEN βœ“

Iteration 2 β€” Must contain uppercase

def test_password_without_uppercase_is_invalid():
    validator = PasswordValidator()
    result = validator.validate("alllowercase1!")
    assert result.is_valid is False
    assert any("uppercase" in e for e in result.errors)

def test_valid_password_passes_all_checks():
    validator = PasswordValidator()
    result = validator.validate("SecurePass1!")
    assert result.is_valid is True
    assert result.errors == []
pytest -v  # RED: test_password_without_uppercase_is_invalid fails
# Add to validate()
if not any(c.isupper() for c in password):
    errors.append("Password must contain at least one uppercase letter")
pytest -v  # GREEN βœ“ β€” both new tests pass, existing tests still pass

Iteration 3 β€” Refactor: extract validation rules

# Refactor: rules become composable β€” behavior unchanged
class PasswordValidator:
    RULES = [
        (lambda p: len(p) >= 8,           "Password must be at least 8 characters"),
        (lambda p: any(c.isupper() for c in p), "Password must contain at least one uppercase letter"),
        (lambda p: any(c.isdigit() for c in p), "Password must contain at least one digit"),
        (lambda p: any(c in "!@#$%^&*" for c in p), "Password must contain at least one special character"),
    ]

    def validate(self, password: str) -> ValidationResult:
        errors = [msg for check, msg in self.RULES if not check(password)]
        return ValidationResult(is_valid=len(errors) == 0, errors=errors)
pytest -v  # GREEN βœ“ β€” all tests pass, new rules covered by existing tests

What to Test vs What to Mock

Test directly:

  • Pure functions (inputs β†’ outputs, no side effects)
  • Business logic and domain rules
  • Data transformations and calculations
  • Edge cases: empty input, max values, invalid types

Mock (replace with a fake):

  • External API calls (HTTP requests, Stripe, Twilio)
  • Database queries
  • File system I/O
  • Clock / time (datetime.now())
  • Random number generation
# Mocking an external API call
from unittest.mock import patch, MagicMock

def test_sends_welcome_email_after_registration():
    user = {"email": "[email protected]", "name": "Alice"}

    with patch("services.email.send_email") as mock_send:
        mock_send.return_value = {"status": "sent"}
        result = register_user(user)

    assert result["success"] is True
    mock_send.assert_called_once_with(
        to="[email protected]",
        subject="Welcome to our platform",
        template="welcome",
    )

TDD for Different Test Types

Unit Tests β€” single function or class

# Fast, no external deps, pure logic
def test_format_currency_rounds_to_two_decimals():
    assert format_currency(1.2345) == "$1.23"
    assert format_currency(0)      == "$0.00"
    assert format_currency(-5.5)   == "-$5.50"

Integration Tests β€” multiple real components

# Slower, uses real DB/filesystem, tests component wiring
def test_user_registration_creates_db_record(test_db):
    register_user({"email": "[email protected]", "name": "Alice"}, db=test_db)
    user = test_db.query("SELECT * FROM users WHERE email = ?", "[email protected]")
    assert user is not None
    assert user["name"] == "Alice"

Run unit and integration tests separately:

pytest tests/unit/ -v           # fast, run on every save
pytest tests/integration/ -v    # slower, run before commit

Running Tests Efficiently

# Run a single test
pytest tests/test_order.py::test_calculate_order_total_applies_discount -v

# Run all tests in a file
pytest tests/test_order.py -v

# Run tests matching a keyword
pytest -k "discount" -v

# Stop on first failure
pytest -x -v

# Show coverage report
pytest --cov=src --cov-report=term-missing tests/

# Watch mode (install pytest-watch)
ptw tests/ -- -v

Common TDD Pitfalls

PitfallSymptomFix
Test passes without any production codeTest was already testing the wrong thingVerify the test fails first, always
Too many things in one testHard to understand why it failedOne behavior per test
Testing implementation detailsTest breaks on refactor even though behavior is correctTest inputs/outputs, not internal method calls
Skipping the refactor stepCode gets messy over timeRefactor is not optional; budget time for it
Giant test setupEvery test has 50 lines of ArrangeExtract fixtures / factories
Mocking too muchTests pass but production system is brokenPrefer integration tests for integration points

TDD Checklist (per feature)

  • Wrote a failing test first (confirmed it is red)
  • Implemented only enough code to pass the test (no speculative features)
  • All tests green after implementation
  • Refactored code β€” tests still green
  • Edge cases covered: empty input, null, boundary values
  • External dependencies mocked in unit tests
  • Coverage report checked for untested branches
  • Tests are readable by a teammate unfamiliar with this code

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.