Why AI for Testing?
Writing tests is often neglected because it's time-consuming. AI changes this by:
Test Generation Workflow
Step 1: Analyze the Function
TypeScript
const testGenerationPrompt = `Analyze this function and generate comprehensive tests.
Function:
${functionCode}
Consider:
1. Happy path scenarios
2. Edge cases (null, empty, boundary values)
3. Error conditions
4. Type edge cases
5. Async behavior (if applicable)
Output Jest/Vitest tests with descriptive names.`;Step 2: Generate Tests
TypeScript
async function generateTests(
functionCode: string,
context: string
): Promise<string> {
const response = await openai.chat.completions.create({
model: 'gpt-4-turbo',
messages: [
{
role: 'system',
content: `You are a senior QA engineer. Generate comprehensive,
meaningful tests. Focus on behavior, not implementation.`
},
{
role: 'user',
content: `Generate tests for this code.
Context (types, dependencies):
${context}
Code to test:
${functionCode}`
}
],
temperature: 0.2
});
return response.choices[0].message.content;
}Example: Generated Tests
Given this function:
TypeScript
function calculateDiscount(
price: number,
customerType: 'regular' | 'vip' | 'wholesale',
quantity: number
): number {
if (price <= 0 || quantity <= 0) {
throw new Error('Invalid input');
}
let discount = 0;
if (customerType === 'vip') discount = 0.2;
else if (customerType === 'wholesale') discount = 0.15;
else if (quantity >= 10) discount = 0.05;
return price * quantity * (1 - discount);
}AI generates:
TypeScript
describe('calculateDiscount', () => {
describe('happy path', () => {
it('should apply no discount for regular customer with small quantity', () => {
expect(calculateDiscount(100, 'regular', 5)).toBe(500);
});
it('should apply 20% discount for VIP customers', () => {
expect(calculateDiscount(100, 'vip', 1)).toBe(80);
});
it('should apply 15% discount for wholesale customers', () => {
expect(calculateDiscount(100, 'wholesale', 1)).toBe(85);
});
it('should apply 5% bulk discount for regular customers with 10+ items', () => {
expect(calculateDiscount(100, 'regular', 10)).toBe(950);
});
});
describe('edge cases', () => {
it('should handle minimum valid quantity', () => {
expect(calculateDiscount(100, 'regular', 1)).toBe(100);
});
it('should handle decimal prices', () => {
expect(calculateDiscount(99.99, 'vip', 1)).toBeCloseTo(79.992);
});
it('should handle quantity at bulk threshold boundary', () => {
expect(calculateDiscount(100, 'regular', 9)).toBe(900);
expect(calculateDiscount(100, 'regular', 10)).toBe(950);
});
});
describe('error handling', () => {
it('should throw for zero price', () => {
expect(() => calculateDiscount(0, 'regular', 1)).toThrow('Invalid input');
});
it('should throw for negative price', () => {
expect(() => calculateDiscount(-10, 'regular', 1)).toThrow('Invalid input');
});
it('should throw for zero quantity', () => {
expect(() => calculateDiscount(100, 'regular', 0)).toThrow('Invalid input');
});
it('should throw for negative quantity', () => {
expect(() => calculateDiscount(100, 'regular', -1)).toThrow('Invalid input');
});
});
});Integration with CI/CD
YAML
name: AI Test Generation
on:
push:
paths:
- 'src/**/*.ts'
jobs:
generate-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Detect changed functions
id: changes
run: |
# Extract changed functions
echo "functions=$(./scripts/detect-changes.sh)" >> $GITHUB_OUTPUT
- name: Generate missing tests
run: |
npx ai-test-gen --files "${{ steps.changes.outputs.functions }}"
- name: Run generated tests
run: npm test
- name: Create PR with new tests
if: success()
uses: peter-evans/create-pull-request@v5
with:
title: 'test: AI-generated tests for new functions'
branch: ai-tests/${{ github.sha }}Test Quality Validation
TypeScript
async function validateTestQuality(tests: string): Promise<ValidationResult> {
// Run mutation testing
const mutationScore = await runMutationTesting(tests);
// Check coverage
const coverage = await runCoverage(tests);
// Analyze test structure
const analysis = await analyzeTestStructure(tests);
return {
mutationScore,
coverage,
hasEdgeCases: analysis.edgeCaseCount > 0,
hasErrorHandling: analysis.errorTestCount > 0,
isReadable: analysis.readabilityScore > 0.8
};
}Best Practices
Recommended Reading
š¬Discussion
šØļø
No comments yet
Be the first to share your thoughts!
