ワンクリックで
test-driven-development
Enforce Red-Green-Refactor TDD workflow for writing tests before implementation
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Enforce Red-Green-Refactor TDD workflow for writing tests before implementation
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Comprehensive code review framework for evaluating code quality, security, performance, and maintainability
Standards and workflows for creating and maintaining high-quality documentation
Manage Git worktrees for parallel development workflows without switching branches
Core Kilo agent configuration with comprehensive coding capabilities and workflow management
A systematic 4-phase debugging framework for reproducing, analyzing, fixing, and verifying bugs
Template for creating new skills - customize and rename for your use case
| name | test-driven-development |
| version | 1.0.0 |
| description | Enforce Red-Green-Refactor TDD workflow for writing tests before implementation |
A disciplined approach to development where tests drive the design and implementation of code.
Use this skill when:
┌─────────┐
│ RED │ ──► Write failing test
└────┬────┘
│
▼
┌─────────┐
│ GREEN │ ──► Write minimal code to pass
└────┬────┘
│
▼
┌──────────┐
│ REFACTOR │ ──► Improve code quality
└─────┬────┘
│
└──► Repeat
Goal: Write a test that fails because the feature doesn't exist yet
Understand the requirement
Write a minimal test
Run the test
#[test]
fn test_<behavior_description>() {
// Arrange - Set up test data
let input = /* test input */;
let expected = /* expected output */;
// Act - Execute the behavior
let result = function_under_test(input);
// Assert - Verify the outcome
assert_eq!(result, expected);
}
// ✅ Good: Test behavior with descriptive name
#[test]
fn test_calculate_total_returns_sum_of_item_prices() {
let items = vec![
Item { name: "A", price: 10.0 },
Item { name: "B", price: 20.0 },
];
let result = calculate_total(&items);
assert_eq!(result, 30.0);
}
// ✅ Good: Test edge case
#[test]
fn test_calculate_total_returns_zero_for_empty_items() {
let items = vec![];
let result = calculate_total(&items);
assert_eq!(result, 0.0);
}
// ✅ Good: Test error case
#[test]
fn test_parse_config_returns_error_for_invalid_toml() {
let invalid_toml = "not valid toml [[[";
let result = parse_config(invalid_toml);
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), ConfigError::InvalidToml(_)));
}
// ❌ Bad: Testing implementation details
#[test]
fn test_internal_parser_helper() {
// Don't test private functions directly
}
// ❌ Bad: Multiple assertions in one test
#[test]
fn test_everything() {
assert_eq!(func1(), 1);
assert_eq!(func2(), 2);
assert_eq!(func3(), 3); // If func2 fails, we don't test func3
}
// Pattern: test_<unit>_<scenario>_<expected_result>
#[test]
fn test_parse_date_with_valid_iso_format_returns_date() { }
#[test]
fn test_parse_date_with_invalid_format_returns_error() { }
#[test]
fn test_parse_date_with_empty_string_returns_error() { }
#[test]
fn test_parse_date_with_future_date_returns_date() { }
Goal: Write the minimal code to make the test pass
Write minimal implementation
Run the test
Keep it simple
// Test
#[test]
fn test_add_returns_sum_of_two_numbers() {
assert_eq!(add(2, 3), 5);
}
// Green (minimal implementation)
fn add(a: i32, b: i32) -> i32 {
5 // Hardcoded! That's OK for now
}
// Later, after more tests...
#[test]
fn test_add_with_negative_numbers() {
assert_eq!(add(-1, 1), 0);
}
// Now we need proper implementation
fn add(a: i32, b: i32) -> i32 {
a + b // Generalized solution
}
// Start with the simplest thing that could possibly work
pub fn format_name(first: &str, last: &str) -> String {
format!("{} {}", first, last) // Simple concatenation
}
// As tests reveal edge cases, refine
#[test]
fn test_format_name_handles_whitespace() {
assert_eq!(format_name(" John ", " Doe "), "John Doe");
}
// Refined implementation
pub fn format_name(first: &str, last: &str) -> String {
format!("{} {}", first.trim(), last.trim())
}
Goal: Improve code quality while keeping tests green
Identify improvements
Make one change at a time
Verify tests still pass
// Before refactor: Duplicate logic
fn process_user(user: &User) -> String {
let name = format!("{} {}", user.first_name.trim(), user.last_name.trim());
name
}
fn process_admin(admin: &Admin) -> String {
let name = format!("{} {}", admin.first_name.trim(), admin.last_name.trim());
name
}
// After refactor: Extract common logic
fn format_name(first: &str, last: &str) -> String {
format!("{} {}", first.trim(), last.trim())
}
fn process_user(user: &User) -> String {
format_name(&user.first_name, &user.last_name)
}
fn process_admin(admin: &Admin) -> String {
format_name(&admin.first_name, &admin.last_name)
}
REFACTORING:
- [ ] Code is readable
- [ ] Names are descriptive
- [ ] Functions are focused
- [ ] No duplication
- [ ] Proper abstraction
- [ ] Tests still pass
1. Write acceptance criteria
2. Break down into small units
3. For each unit:
a. RED: Write failing test
b. GREEN: Make it pass
c. REFACTOR: Clean up
4. Integration test
5. Manual verification
1. Write test that reproduces bug
- Test should fail (demonstrating bug exists)
2. Fix the code
- Test should now pass
3. Refactor if needed
4. Verify no regression
// Step 1: Write test that demonstrates bug
#[test]
fn test_parse_json_handles_empty_array() {
let json = "[]";
let result = parse_json(json);
// This fails because current implementation panics on empty array
assert_eq!(result.unwrap(), vec![]);
}
// Step 2: Run test - it fails (RED)
// thread 'test_parse_json_handles_empty_array' panicked at 'index out of bounds'
// Step 3: Fix the code (GREEN)
fn parse_json(json: &str) -> Result<Vec<Item>> {
let items: Vec<Item> = serde_json::from_str(json)?;
Ok(items) // Now handles empty array correctly
}
// Step 4: Run test - it passes (GREEN)
// Step 5: Refactor if needed (REFACTOR)
// Code is clean, no refactor needed
// Step 6: Run all tests
cargo test // All tests pass
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_single_unit_of_work() {
// Fast, isolated, focused
let result = parse_input("test");
assert!(result.is_ok());
}
}
// tests/integration_test.rs
use ktme::*;
use tempfile::TempDir;
#[test]
fn test_full_workflow() {
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().join("test.db");
// Initialize
let db = Database::new(&db_path).unwrap();
// Create
let service = db.create_service("test-service").unwrap();
// Read
let retrieved = db.get_service("test-service").unwrap();
assert_eq!(service.name, retrieved.unwrap().name);
// Update
db.update_service_description("test-service", "Updated").unwrap();
// Delete
db.delete_service("test-service").unwrap();
}
use proptest::prelude::*;
proptest! {
#[test]
fn test_parse_format_roundtrip(s in "\\PC*") {
// For any string, parsing and formatting should be reversible
let parsed = parse_input(&s);
if let Ok(value) = parsed {
let formatted = format_output(&value);
let reparsed = parse_input(&formatted).unwrap();
assert_eq!(value, reparsed);
}
}
}
src/
├── lib.rs
├── parser.rs
│ └── #[cfg(test)] mod tests { }
└── database.rs
└── #[cfg(test)] mod tests { }
tests/
├── integration_test.rs
├── common/
│ └── mod.rs
└── fixtures/
└── test_data.json
// src/parser.rs
pub fn parse_input(input: &str) -> Result<ParsedData> {
// Implementation
}
#[cfg(test)]
mod tests {
use super::*;
mod parse_input {
use super::*;
#[test]
fn with_valid_input_returns_parsed_data() { }
#[test]
fn with_empty_input_returns_error() { }
#[test]
fn with_invalid_format_returns_error() { }
}
mod edge_cases {
use super::*;
#[test]
fn handles_unicode_correctly() { }
#[test]
fn handles_very_long_input() { }
}
}
// ❌ Bad: Tests depend on each other
static mut COUNTER: i32 = 0;
#[test]
fn test_increment() {
unsafe { COUNTER += 1; }
assert_eq!(unsafe { COUNTER }, 1);
}
#[test]
fn test_increment_again() {
unsafe { COUNTER += 1; }
assert_eq!(unsafe { COUNTER }, 2); // Depends on test order!
}
// ✅ Good: Each test is independent
#[test]
fn test_increment() {
let mut counter = 0;
counter += 1;
assert_eq!(counter, 1);
}
#[test]
fn test_increment_again() {
let mut counter = 0;
counter += 1;
assert_eq!(counter, 1); // Independent of other tests
}
// ❌ Bad: Unclear test
#[test]
fn test1() {
let x = func("a", "b");
assert!(x);
}
// ✅ Good: Clear test
#[test]
fn test_validate_credentials_returns_true_for_valid_user() {
let username = "valid_user";
let password = "valid_pass123";
let is_valid = validate_credentials(username, password);
assert!(is_valid);
}
#[test]
fn test_user_can_be_created_with_valid_data() {
// Arrange - Set up test data
let name = "John Doe";
let email = "john@example.com";
// Act - Perform the action
let user = User::new(name, email);
// Assert - Verify the result
assert_eq!(user.name, name);
assert_eq!(user.email, email);
}
# Run all tests
cargo test
# Run specific test
cargo test test_parse_input
# Run tests in specific module
cargo test parser::tests
# Run tests matching pattern
cargo test "test_parse"
# Show println! output
cargo test -- --nocapture
# Run single test by name
cargo test test_specific_function --exact
# Run tests in parallel
cargo test -- --test-threads=4
# Run ignored tests
cargo test -- --ignored
❌ Don't:
✅ Do:
This skill integrates with Kilo's workflow:
Use with other skills: