-
Couldn't load subscription status.
- Fork 0
TDD Implementation Guide
#AutoSDLC #TDD #Testing #Implementation
← Back to Index | ← Testing Strategy
This guide provides a comprehensive approach to Test-Driven Development (TDD) in the AutoSDLC system. Our TDD implementation is strict: tests are written first based on product specifications, verified to fail completely, and only then is implementation code written. No mocks are used - all tests work with real implementations.
- Tests Are Specifications: Tests define the exact behavior expected
- Red-Green-Refactor: The only acceptable workflow
- No Mocks Policy: All tests use real implementations
- Coverage Is Mandatory: 100% coverage of product specifications
- Tests Drive Design: Implementation emerges from test requirements
graph LR
A[Product Spec] --> B[Write Tests]
B --> C[Verify Red]
C --> D{All Tests Fail?}
D -->|No| E[Fix Tests]
E --> C
D -->|Yes| F[Implement Code]
F --> G[Run Tests]
G --> H{Tests Pass?}
H -->|No| I[Fix Implementation]
I --> G
H -->|Yes| J[Refactor]
J --> K[Run Tests]
K --> L{Still Green?}
L -->|No| M[Fix Refactoring]
M --> K
L -->|Yes| N[Complete]
// Example: User Authentication Specification
interface AuthenticationSpec {
title: "User Authentication System";
requirements: [
"Users can register with email and password",
"Passwords must be at least 8 characters with mixed case, numbers, and symbols",
"Users can login with valid credentials",
"System generates JWT tokens on successful login",
"Tokens expire after 24 hours",
"Users can refresh tokens before expiry",
"Failed login attempts are rate-limited"
];
acceptanceCriteria: [
"Registration fails with invalid email format",
"Registration fails with weak passwords",
"Login fails with incorrect credentials",
"Login succeeds with correct credentials",
"JWT tokens are valid and contain user information",
"Expired tokens are rejected",
"5 failed login attempts trigger 15-minute lockout"
];
}
// Transform specification into comprehensive tests
describe('User Authentication System', () => {
let authService: AuthService;
let database: Database;
let tokenService: TokenService;
beforeAll(async () => {
// Setup real services - NO MOCKS
database = new Database({
connection: process.env.TEST_DATABASE_URL
});
await database.connect();
tokenService = new TokenService({
secret: process.env.TEST_JWT_SECRET
});
authService = new AuthService({
database,
tokenService
});
});
afterAll(async () => {
await database.disconnect();
});
beforeEach(async () => {
// Clean database before each test
await database.users.deleteAll();
await database.loginAttempts.deleteAll();
});
describe('User Registration', () => {
it('should allow registration with valid email and strong password', async () => {
const result = await authService.register({
email: 'user@example.com',
password: 'StrongPass123!'
});
expect(result.success).toBe(true);
expect(result.user).toBeDefined();
expect(result.user.id).toBeDefined();
expect(result.user.email).toBe('user@example.com');
// Verify user exists in real database
const dbUser = await database.users.findByEmail('user@example.com');
expect(dbUser).toBeDefined();
expect(dbUser.passwordHash).toBeDefined();
expect(dbUser.passwordHash).not.toBe('StrongPass123!'); // Should be hashed
});
it('should reject registration with invalid email format', async () => {
const invalidEmails = [
'notanemail',
'missing@tld',
'@example.com',
'user@',
'user..name@example.com',
'user name@example.com'
];
for (const email of invalidEmails) {
const result = await authService.register({
email,
password: 'ValidPass123!'
});
expect(result.success).toBe(false);
expect(result.error).toContain('Invalid email format');
// Verify no user created in database
const dbUser = await database.users.findByEmail(email);
expect(dbUser).toBeNull();
}
});
it('should enforce password complexity requirements', async () => {
const weakPasswords = [
{ password: 'short', error: 'at least 8 characters' },
{ password: 'alllowercase', error: 'uppercase letter' },
{ password: 'ALLUPPERCASE', error: 'lowercase letter' },
{ password: 'NoNumbers!', error: 'number' },
{ password: 'NoSymbols123', error: 'special character' },
{ password: '12345678', error: 'letters' }
];
for (const { password, error } of weakPasswords) {
const result = await authService.register({
email: 'user@example.com',
password
});
expect(result.success).toBe(false);
expect(result.error).toContain(error);
}
});
it('should prevent duplicate email registration', async () => {
// First registration
await authService.register({
email: 'existing@example.com',
password: 'FirstPass123!'
});
// Attempt duplicate
const result = await authService.register({
email: 'existing@example.com',
password: 'SecondPass123!'
});
expect(result.success).toBe(false);
expect(result.error).toContain('Email already registered');
});
});
describe('User Login', () => {
beforeEach(async () => {
// Create test user
await authService.register({
email: 'test@example.com',
password: 'TestPass123!'
});
});
it('should allow login with correct credentials', async () => {
const result = await authService.login({
email: 'test@example.com',
password: 'TestPass123!'
});
expect(result.success).toBe(true);
expect(result.token).toBeDefined();
expect(result.refreshToken).toBeDefined();
expect(result.expiresIn).toBe(86400); // 24 hours
// Verify token is valid JWT
const decoded = tokenService.verify(result.token);
expect(decoded.userId).toBeDefined();
expect(decoded.email).toBe('test@example.com');
expect(decoded.exp).toBeGreaterThan(Date.now() / 1000);
});
it('should reject login with incorrect password', async () => {
const result = await authService.login({
email: 'test@example.com',
password: 'WrongPassword123!'
});
expect(result.success).toBe(false);
expect(result.error).toBe('Invalid credentials');
expect(result.token).toBeUndefined();
// Verify login attempt recorded
const attempts = await database.loginAttempts.findByEmail('test@example.com');
expect(attempts.length).toBe(1);
expect(attempts[0].successful).toBe(false);
});
it('should reject login with non-existent email', async () => {
const result = await authService.login({
email: 'nonexistent@example.com',
password: 'AnyPass123!'
});
expect(result.success).toBe(false);
expect(result.error).toBe('Invalid credentials');
});
});
describe('Rate Limiting', () => {
it('should lock account after 5 failed login attempts', async () => {
// Register user
await authService.register({
email: 'ratelimit@example.com',
password: 'CorrectPass123!'
});
// Make 5 failed attempts
for (let i = 0; i < 5; i++) {
await authService.login({
email: 'ratelimit@example.com',
password: 'WrongPass123!'
});
}
// 6th attempt should be rate limited
const result = await authService.login({
email: 'ratelimit@example.com',
password: 'CorrectPass123!' // Even with correct password
});
expect(result.success).toBe(false);
expect(result.error).toContain('Too many failed attempts');
expect(result.retryAfter).toBe(900); // 15 minutes in seconds
// Verify lockout in database
const user = await database.users.findByEmail('ratelimit@example.com');
expect(user.lockedUntil).toBeDefined();
expect(user.lockedUntil.getTime()).toBeGreaterThan(Date.now());
});
it('should reset failed attempts after successful login', async () => {
await authService.register({
email: 'reset@example.com',
password: 'CorrectPass123!'
});
// Make 4 failed attempts
for (let i = 0; i < 4; i++) {
await authService.login({
email: 'reset@example.com',
password: 'WrongPass123!'
});
}
// Successful login
await authService.login({
email: 'reset@example.com',
password: 'CorrectPass123!'
});
// Failed attempts should be reset
const attempts = await database.loginAttempts.countFailed('reset@example.com');
expect(attempts).toBe(0);
});
});
describe('Token Management', () => {
it('should generate valid JWT tokens', async () => {
await authService.register({
email: 'token@example.com',
password: 'TokenPass123!'
});
const loginResult = await authService.login({
email: 'token@example.com',
password: 'TokenPass123!'
});
// Manually verify token structure
const parts = loginResult.token.split('.');
expect(parts.length).toBe(3); // Header.Payload.Signature
// Decode and verify payload
const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString());
expect(payload.email).toBe('token@example.com');
expect(payload.iat).toBeDefined();
expect(payload.exp).toBeDefined();
expect(payload.exp - payload.iat).toBe(86400); // 24 hours
});
it('should reject expired tokens', async () => {
// Create an expired token
const expiredToken = tokenService.sign(
{ userId: '123', email: 'test@example.com' },
{ expiresIn: '-1h' } // Expired 1 hour ago
);
const result = await authService.validateToken(expiredToken);
expect(result.valid).toBe(false);
expect(result.error).toBe('Token expired');
});
it('should allow token refresh before expiry', async () => {
await authService.register({
email: 'refresh@example.com',
password: 'RefreshPass123!'
});
const loginResult = await authService.login({
email: 'refresh@example.com',
password: 'RefreshPass123!'
});
// Wait a moment to ensure new token has different timestamp
await new Promise(resolve => setTimeout(resolve, 1000));
const refreshResult = await authService.refreshToken(loginResult.refreshToken);
expect(refreshResult.success).toBe(true);
expect(refreshResult.token).toBeDefined();
expect(refreshResult.token).not.toBe(loginResult.token);
// Verify new token is valid
const decoded = tokenService.verify(refreshResult.token);
expect(decoded.email).toBe('refresh@example.com');
});
});
});#!/bin/bash
# verify-all-tests-red.sh
echo "=== TDD Phase 1: Verifying all tests are RED ==="
# Run all tests without implementation
npm test -- --no-coverage --json > test-results.json
# Extract results
TOTAL=$(jq '.numTotalTests' test-results.json)
FAILED=$(jq '.numFailedTests' test-results.json)
PASSED=$(jq '.numPassedTests' test-results.json)
echo "Test Results:"
echo " Total Tests: $TOTAL"
echo " Failed Tests: $FAILED"
echo " Passed Tests: $PASSED"
# Verify all tests fail
if [ "$PASSED" -ne 0 ]; then
echo ""
echo "ERROR: Some tests are passing without implementation!"
echo "This violates TDD principles. The following tests passed:"
jq '.testResults[].assertionResults[] | select(.status == "passed") | .title' test-results.json
exit 1
fi
if [ "$FAILED" -ne "$TOTAL" ]; then
echo ""
echo "ERROR: Not all tests are failing!"
echo "Expected $TOTAL failures, got $FAILED"
exit 1
fi
echo ""
echo "✓ SUCCESS: All $TOTAL tests are failing (RED state confirmed)"
echo "✓ Ready to proceed with implementation"
# Generate coverage report of specifications
echo ""
echo "=== Specification Coverage Analysis ==="
node scripts/analyze-test-coverage.js
exit 0// scripts/analyze-test-coverage.js
const fs = require('fs');
const path = require('path');
class SpecificationCoverageAnalyzer {
constructor(specFile, testFile) {
this.specification = require(specFile);
this.tests = this.parseTestFile(testFile);
}
parseTestFile(testFile) {
const content = fs.readFileSync(testFile, 'utf-8');
const tests = [];
// Extract test descriptions
const testMatches = content.matchAll(/it\(['"`](.+?)['"`]/g);
for (const match of testMatches) {
tests.push(match[1]);
}
return tests;
}
analyzeCoverage() {
const coveredRequirements = new Set();
const coveredCriteria = new Set();
// Map tests to requirements
this.tests.forEach(test => {
this.specification.requirements.forEach((req, index) => {
if (this.testCoversRequirement(test, req)) {
coveredRequirements.add(index);
}
});
this.specification.acceptanceCriteria.forEach((criteria, index) => {
if (this.testCoversCriteria(test, criteria)) {
coveredCriteria.add(index);
}
});
});
// Generate coverage report
const requirementCoverage = (coveredRequirements.size / this.specification.requirements.length) * 100;
const criteriaCoverage = (coveredCriteria.size / this.specification.acceptanceCriteria.length) * 100;
console.log('\n=== Specification Coverage Report ===');
console.log(`Requirements Coverage: ${requirementCoverage.toFixed(1)}%`);
console.log(`Acceptance Criteria Coverage: ${criteriaCoverage.toFixed(1)}%`);
// List uncovered items
if (requirementCoverage < 100) {
console.log('\nUncovered Requirements:');
this.specification.requirements.forEach((req, index) => {
if (!coveredRequirements.has(index)) {
console.log(` - ${req}`);
}
});
}
if (criteriaCoverage < 100) {
console.log('\nUncovered Acceptance Criteria:');
this.specification.acceptanceCriteria.forEach((criteria, index) => {
if (!coveredCriteria.has(index)) {
console.log(` - ${criteria}`);
}
});
}
return {
requirementCoverage,
criteriaCoverage,
totalCoverage: (requirementCoverage + criteriaCoverage) / 2
};
}
testCoversRequirement(test, requirement) {
// Implement logic to determine if a test covers a requirement
const testLower = test.toLowerCase();
const reqLower = requirement.toLowerCase();
// Extract key words from requirement
const keywords = reqLower.split(' ')
.filter(word => word.length > 3)
.filter(word => !['with', 'must', 'should', 'users', 'system'].includes(word));
// Check if test description contains key words
return keywords.some(keyword => testLower.includes(keyword));
}
testCoversCriteria(test, criteria) {
// Similar logic for acceptance criteria
return this.testCoversRequirement(test, criteria);
}
}
// Run analyzer
const analyzer = new SpecificationCoverageAnalyzer(
'./specifications/auth-spec.json',
'./tests/auth.test.ts'
);
const coverage = analyzer.analyzeCoverage();
if (coverage.totalCoverage < 100) {
console.error('\n❌ ERROR: Not all specifications are covered by tests!');
process.exit(1);
}
console.log('\n✅ All specifications are covered by tests');// Example: Implementing to pass tests
export class AuthService {
constructor(
private database: Database,
private tokenService: TokenService,
private cryptoService: CryptoService
) {}
async register(credentials: RegisterCredentials): Promise<RegisterResult> {
// Step 1: Validate email format
if (!this.isValidEmail(credentials.email)) {
return {
success: false,
error: 'Invalid email format'
};
}
// Step 2: Validate password strength
const passwordValidation = this.validatePassword(credentials.password);
if (!passwordValidation.valid) {
return {
success: false,
error: passwordValidation.error
};
}
// Step 3: Check for existing user
const existingUser = await this.database.users.findByEmail(credentials.email);
if (existingUser) {
return {
success: false,
error: 'Email already registered'
};
}
// Step 4: Hash password and create user
const passwordHash = await this.cryptoService.hashPassword(credentials.password);
const user = await this.database.users.create({
email: credentials.email,
passwordHash,
createdAt: new Date()
});
return {
success: true,
user: {
id: user.id,
email: user.email
}
};
}
private isValidEmail(email: string): boolean {
// RFC 5322 compliant email regex
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Additional validations
if (!emailRegex.test(email)) return false;
if (email.includes('..')) return false;
if (email.startsWith('.') || email.endsWith('.')) return false;
const [localPart, domain] = email.split('@');
if (localPart.length > 64) return false;
if (domain.length > 255) return false;
return true;
}
private validatePassword(password: string): PasswordValidation {
const errors = [];
if (password.length < 8) {
errors.push('Password must be at least 8 characters');
}
if (!/[A-Z]/.test(password)) {
errors.push('Password must contain an uppercase letter');
}
if (!/[a-z]/.test(password)) {
errors.push('Password must contain a lowercase letter');
}
if (!/[0-9]/.test(password)) {
errors.push('Password must contain a number');
}
if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
errors.push('Password must contain a special character');
}
return {
valid: errors.length === 0,
error: errors.join(', ')
};
}
}#!/bin/bash
# progressive-implementation.sh
# Run tests continuously during implementation
npm test -- --watch --bail --verbose
# After each small change:
# 1. Save file
# 2. Check test output
# 3. Only proceed if making progress
# 4. If tests regress, undo last change// Before refactoring - ensure all tests pass
describe('Pre-refactoring verification', () => {
it('should have all tests passing', async () => {
const result = await runAllTests();
expect(result.failedTests).toBe(0);
expect(result.passedTests).toBe(result.totalTests);
});
it('should have adequate test coverage', async () => {
const coverage = await getCoverage();
expect(coverage.statements).toBeGreaterThan(80);
expect(coverage.branches).toBeGreaterThan(80);
expect(coverage.functions).toBeGreaterThan(80);
expect(coverage.lines).toBeGreaterThan(80);
});
});
// Refactoring improvements
class RefactoredAuthService {
// Extract validation logic
private validators = {
email: new EmailValidator(),
password: new PasswordValidator()
};
// Simplify main method
async register(credentials: RegisterCredentials): Promise<RegisterResult> {
// Validate inputs
const validation = await this.validateRegistration(credentials);
if (!validation.valid) {
return { success: false, error: validation.error };
}
// Check uniqueness
if (await this.emailExists(credentials.email)) {
return { success: false, error: 'Email already registered' };
}
// Create user
const user = await this.createUser(credentials);
return { success: true, user };
}
// Extract complex logic into focused methods
private async validateRegistration(credentials: RegisterCredentials): Promise<ValidationResult> {
const emailValidation = this.validators.email.validate(credentials.email);
if (!emailValidation.valid) {
return emailValidation;
}
const passwordValidation = this.validators.password.validate(credentials.password);
return passwordValidation;
}
private async emailExists(email: string): Promise<boolean> {
const user = await this.database.users.findByEmail(email);
return user !== null;
}
private async createUser(credentials: RegisterCredentials): Promise<User> {
const passwordHash = await this.cryptoService.hashPassword(credentials.password);
return await this.database.users.create({
email: credentials.email,
passwordHash,
createdAt: new Date()
});
}
}# .github/workflows/tdd-enforcement.yml
name: TDD Enforcement
on:
pull_request:
types: [opened, synchronize]
jobs:
verify-tdd-compliance:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Full history for commit analysis
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Verify test-first approach
run: |
# Check commit history
./scripts/verify-tdd-commits.sh ${{ github.event.pull_request.base.sha }}
- name: Check test coverage
run: |
npm test -- --coverage
./scripts/verify-coverage.sh
- name: Verify no mocks
run: |
./scripts/check-for-mocks.sh
- name: Generate TDD report
run: |
./scripts/generate-tdd-report.sh > tdd-report.md
- name: Comment on PR
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('tdd-report.md', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: report
});// metrics/TDDMetrics.ts
export class TDDMetricsCollector {
async collectMetrics(projectPath: string): Promise<TDDMetrics> {
return {
testFirstCompliance: await this.calculateTestFirstCompliance(projectPath),
testCoverage: await this.getTestCoverage(projectPath),
mockUsage: await this.detectMockUsage(projectPath),
redGreenRefactorCycles: await this.countTDDCycles(projectPath),
averageTestSize: await this.calculateAverageTestSize(projectPath),
testToCodeRatio: await this.calculateTestToCodeRatio(projectPath)
};
}
private async calculateTestFirstCompliance(projectPath: string): Promise<number> {
const commits = await this.getCommitHistory(projectPath);
let testFirstCount = 0;
let totalFeatures = 0;
for (const feature of this.extractFeatures(commits)) {
totalFeatures++;
const testCommit = this.findFirstTestCommit(feature, commits);
const implCommit = this.findFirstImplCommit(feature, commits);
if (testCommit && implCommit && testCommit.date < implCommit.date) {
testFirstCount++;
}
}
return (testFirstCount / totalFeatures) * 100;
}
private async detectMockUsage(projectPath: string): Promise<MockUsage> {
const testFiles = await this.findTestFiles(projectPath);
const mockPatterns = [
/jest\.mock/,
/sinon\./,
/td\.replace/,
/proxyquire/,
/mockery/,
/@Mock/,
/createMock/
];
let filesWithMocks = 0;
let totalMockCalls = 0;
for (const file of testFiles) {
const content = await fs.readFile(file, 'utf-8');
let fileMockCount = 0;
for (const pattern of mockPatterns) {
const matches = content.match(pattern);
if (matches) {
fileMockCount += matches.length;
}
}
if (fileMockCount > 0) {
filesWithMocks++;
totalMockCalls += fileMockCount;
}
}
return {
filesWithMocks,
totalMockCalls,
percentage: (filesWithMocks / testFiles.length) * 100
};
}
}// PM Agent: Creating TDD tasks
class PMAgent {
async createTDDTask(requirement: Requirement): Promise<TDDTask> {
// 1. Create test specification
const testSpec = await this.generateTestSpecification(requirement);
// 2. Create GitHub issue with test spec
const issue = await this.github.createIssue({
title: `TDD: ${requirement.title}`,
body: this.formatTDDIssue(requirement, testSpec),
labels: ['tdd', 'implementation', requirement.priority],
assignees: ['coder-agent']
});
// 3. Create test file
await this.createTestFile(testSpec, requirement);
// 4. Verify tests fail
const verificationResult = await this.verifyTestsFail(testSpec.filePath);
if (!verificationResult.allFailing) {
throw new Error('Some tests are not failing - TDD violation!');
}
// 5. Update Agent_Output.md
await this.updateStatus({
task: 'TDD Task Created',
issue: issue.number,
testFile: testSpec.filePath,
status: 'Tests verified as RED',
assignedTo: 'coder-agent'
});
return {
issueNumber: issue.number,
testSpecPath: testSpec.filePath,
requirement: requirement,
status: 'ready_for_implementation'
};
}
}// Coder Agent: Implementing with TDD
class CoderAgent {
async implementFeature(task: TDDTask): Promise<Implementation> {
// Phase 1: Verify Red
await this.verifyRedPhase(task.testSpecPath);
// Phase 2: Implement Green
const implementation = await this.implementGreenPhase(task);
// Phase 3: Refactor
const refactored = await this.refactorPhase(implementation);
// Create PR
const pr = await this.createPullRequest(refactored, task);
return {
pullRequest: pr,
testResults: await this.getFinalTestResults(),
coverage: await this.getCoverageReport()
};
}
private async implementGreenPhase(task: TDDTask): Promise<Code> {
const tests = await this.parseTests(task.testSpecPath);
const implementation = new Implementation();
for (const test of tests) {
// Update status
await this.updateStatus({
phase: 'GREEN',
currentTest: test.name,
progress: this.calculateProgress(test, tests)
});
// Implement just enough to pass this test
const code = await this.implementForTest(test);
implementation.add(code);
// Verify test passes
const result = await this.runSingleTest(test);
if (!result.passed) {
throw new Error(`Failed to make test pass: ${test.name}`);
}
}
return implementation;
}
}- Write descriptive test names that explain the behavior
- One assertion per test when possible
- Test behavior, not implementation
- Include edge cases and error scenarios
- Use real data that reflects production scenarios
- Never write code without a failing test
- Implement the simplest solution first
- Refactor only when all tests are green
- Keep test and implementation commits separate
- Document TDD phases in commit messages
- Automate red phase verification
- Enforce coverage thresholds
- Block merges without full test passes
- Monitor TDD compliance metrics
- Regular TDD process audits
- Share test specifications before implementation
- Review tests as thoroughly as implementation
- Pair on complex test scenarios
- Maintain shared testing utilities
- Document testing patterns
Solution: Use commit hooks to verify test files are created first
#!/bin/bash
# .git/hooks/pre-commit
# Prevent implementation without tests
if git diff --cached --name-only | grep -E '\.(ts|js)
| grep -v test; then
if ! git diff --cached --name-only | grep -E '\.test\.(ts|js)
; then
echo "Error: Implementation files detected without corresponding tests"
echo "Please commit tests first (TDD)"
exit 1
fi
fiSolution: Provide test utilities for real implementations
// test-utils/real-implementations.ts
export class TestDatabase {
private data = new Map();
async connect() { /* Real connection logic */ }
async insert(table: string, data: any) { /* Real insert */ }
async find(table: string, query: any) { /* Real query */ }
async cleanup() { /* Real cleanup */ }
}
export class TestEmailService {
private sentEmails = [];
async send(email: Email) {
// Real email sending logic (to test inbox)
this.sentEmails.push(email);
return { id: generateId(), status: 'sent' };
}
getSentEmails() { return this.sentEmails; }
}Solution: Specification-driven test generation
// Generate tests from specifications
class TestGenerator {
generateFromSpec(spec: Specification): string {
const tests = [];
// Generate tests for each requirement
for (const req of spec.requirements) {
tests.push(this.generateRequirementTest(req));
}
// Generate tests for each acceptance criteria
for (const criteria of spec.acceptanceCriteria) {
tests.push(this.generateCriteriaTest(criteria));
}
// Generate edge case tests
tests.push(...this.generateEdgeCaseTests(spec));
return this.formatTestSuite(tests);
}
}interface TDDDashboard {
compliance: {
testFirstRate: number; // % of features with tests written first
mockFreeRate: number; // % of tests without mocks
coverageRate: number; // Average test coverage
redPhaseCompliance: number; // % following proper red phase
};
trends: {
daily: TDDMetrics[];
weekly: TDDMetrics[];
monthly: TDDMetrics[];
};
violations: {
recent: Violation[];
byAgent: Map<string, Violation[]>;
byType: Map<ViolationType, number>;
};
}#!/bin/bash
# generate-tdd-report.sh
echo "# TDD Compliance Report"
echo "Generated: $(date)"
echo ""
# Test-first compliance
echo "## Test-First Compliance"
git log --pretty=format:"%h %s" | ./scripts/analyze-tdd-commits.sh
# Mock usage
echo "## Mock Usage Analysis"
find . -name "*.test.ts" -o -name "*.test.js" | xargs grep -l "mock\|Mock\|stub" | wc -l
# Coverage trends
echo "## Coverage Trends"
npm test -- --coverage --json | jq '.coverageMap'
# Red phase violations
echo "## Red Phase Violations"
grep -r "TDD violation" logs/agent-*.log | tail -20Test-Driven Development is not just a testing strategy in AutoSDLC—it's the fundamental methodology that ensures quality, maintainability, and correct implementation of requirements. By following these strict TDD principles:
- Always write tests first based on specifications
- Verify all tests fail before implementing
- Write minimal code to pass tests
- Never use mocks—work with real implementations
- Refactor only when tests are green
The AutoSDLC system ensures that every feature is built correctly from the ground up, with comprehensive test coverage and high confidence in the implementation.
Tags: #AutoSDLC #TDD #Testing #Implementation #BestPractices Last Updated: 2025-06-09