Facilitation of Test-Driven Development using the red–green–refactor cycle. Guides users through writing tests first, implementing the minimal code needed to pass those tests, and refactoring for quality. Use when users want to practice TDD, need help writing tests before code, are developing new features test-first, or want guidance on test structure and implementation. Triggers include phrases like "use TDD," "test-driven development," "write tests first," "red-green-refactor," or requests to develop functionality with accompanying tests.
A comprehensive skill for practicing Test-Driven Development with systematic guidance through the red-green-refactor cycle.
This skill provides structured guidance for Test-Driven Development (TDD), helping you write tests first, implement minimal code to pass those tests, and then refactor with confidence. It supports multiple programming languages with specific guidance for Python and Emacs Lisp.
🔴 RED - Write a Failing Test
🟢 GREEN - Make It Pass
🔵 REFACTOR - Improve the Code
🔁 REPEAT
See references/python-tdd.md for comprehensive Python TDD guide.
cl-letfSee references/elisp-tdd.md for comprehensive Elisp TDD guide.
See references/general-tdd.md for language-agnostic guidance.
Generate test file boilerplate for different languages:
# Python test template
python scripts/test_template_generator.py --language python --module calculator
# Elisp test template
python scripts/test_template_generator.py --language elisp --module my-package
# JavaScript test template
python scripts/test_template_generator.py --language javascript --module utils
# Save to file
python scripts/test_template_generator.py --language python --module mymod --output test_mymod.py
Analyze test coverage and identify gaps:
# Analyze coverage report
python scripts/coverage_analyzer.py analyze coverage.xml
# Get suggestions for next tests
python scripts/coverage_analyzer.py suggest coverage.xml --min-coverage 80
# Different formats
python scripts/coverage_analyzer.py analyze coverage.json --format json
Comprehensive pytest template with:
Location: assets/templates/python_test_template.py
Comprehensive ERT template with:
cl-letfunwind-protectLocation: assets/templates/elisp_test_template.el
Comprehensive checklist covering:
Location: assets/templates/test_checklist.md
Template for documenting TDD sessions with:
Location: assets/templates/tdd_session_log.md
references/python-tdd.mdComplete Python TDD guide covering pytest, unittest, fixtures, parametrized tests, mocking, async testing, and best practices.
references/elisp-tdd.mdComplete Emacs Lisp TDD guide covering ERT, Buttercup, buffer testing, mocking, and CI/CD integration.
references/general-tdd.mdUniversal TDD principles applicable to any language, including finding frameworks, test structure, naming conventions, and common anti-patterns.
references/test-design-patterns.mdComprehensive guide to what to test, test organization, test smells, mocking strategies, and coverage goals.
references/refactoring-with-tests.mdSafe refactoring process with tests as safety net, including common refactoring patterns and when to refactor.
Cycle 1: Addition
# 🔴 RED - test_calculator.py
def test_add_two_positive_numbers():
calc = Calculator()
assert calc.add(2, 3) == 5
# Run: ❌ NameError: Calculator not defined
# 🟢 GREEN - calculator.py
class Calculator:
def add(self, a, b):
return a + b
# Run: ✅ All tests pass
# 🔵 REFACTOR - Add docstrings
class Calculator:
"""Simple calculator for basic arithmetic."""
def add(self, a, b):
"""Add two numbers and return the sum."""
return a + b
# Run: ✅ All tests still pass
# Commit: "Add Calculator.add() method"
Cycle 2: Edge Case - Negative Numbers
# 🔴 RED
def test_add_negative_numbers():
calc = Calculator()
assert calc.add(-2, -3) == -5
# Run: ✅ Already passes (implementation handles it)
# Good! Test documents behavior.
Cycle 3: Error Handling
# 🔴 RED
def test_add_non_numeric_raises_error():
calc = Calculator()
with pytest.raises(TypeError):
calc.add("2", 3)
# Run: ❌ Test fails (no type checking)
# 🟢 GREEN
def add(self, a, b):
"""Add two numbers and return the sum."""
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("Arguments must be numbers")
return a + b
# Run: ✅ All tests pass
# Commit: "Add type checking to Calculator.add()"
Cycle 1: Reverse String
;; 🔴 RED - test-string-utils.el
(ert-deftest test-reverse-string ()
"Test reversing a string."
(should (string= (reverse-string "hello") "olleh")))
;; Run: ❌ void-function reverse-string
;; 🟢 GREEN - string-utils.el
(defun reverse-string (s)
"Reverse string S."
(concat (reverse (string-to-list s))))
;; Run: ✅ Test passes
;; 🔵 REFACTOR - Add error handling
(defun reverse-string (s)
"Reverse string S.
S must be a string. Returns reversed string.
Signals error if S is not a string."
(unless (stringp s)
(error "Argument must be a string"))
(concat (reverse (string-to-list s))))
;; Run: ✅ Test still passes
;; Add test for error case:
(ert-deftest test-reverse-string-error ()
(should-error (reverse-string 123) :type 'error))
;; Commit: "Add reverse-string with error handling"
Problem: Implementing more than needed to pass test Fix: Write minimal code to make test pass
Problem: Tests break when refactoring Fix: Test through public interface
Problem: Not enough coverage, bugs slip through Fix: Write test for every behavior
Problem: Long feedback loop, hard to locate bugs Fix: Run tests after every change
Problem: Test suite becomes meaningless Fix: Keep all tests green or fix immediately
- name: Run tests
run: pytest --cov=mymodule --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v3
- name: Run tests
run: |
emacs -batch -l ert -l my-package.el -l test/test-my-package.el \
-f ert-run-tests-batch-and-exit
references/general-tdd.md to understand principlesQ: Do I need 100% coverage? A: No. Aim for 80-90% of critical code. Focus on business logic, not simple getters.
Q: Should I test private methods? A: No. Test through public interface. If private method needs testing, it might need to be public or extracted.
Q: How do I test legacy code? A: Add characterization tests first, then refactor. See "Working Effectively with Legacy Code" by Michael Feathers.
Q: TDD feels slow at first? A: Yes, but speeds up as you master it. Prevents debugging time later.
Q: What if I don't know how to design it yet? A: TDD helps you discover the design. Let tests guide your API.
Books:
Online:
To improve this skill:
This skill is part of the skillz project and follows the same license.
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer