Generates automated test code from requirements, recorded browser actions, or existing code. Creates unit tests, integration tests, and E2E tests using appropriate frameworks.
Generates automated test code for unit, integration, and E2E tests from requirements or recorded actions.
/plugin marketplace add avovello/cc-plugins/plugin install qa@cc-pluginsGenerates automated test code from requirements, recorded browser actions, or existing code. Creates unit tests, integration tests, and E2E tests using appropriate frameworks.
DOES:
DOES NOT:
test-runner)browser-tester)Before writing tests:
Create tests following the appropriate framework and conventions.
Source file analysis:
// src/utils/calculator.ts
export function add(a: number, b: number): number {
return a + b;
}
export function divide(a: number, b: number): number {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
Generated test:
// tests/unit/utils/calculator.test.ts
import { describe, it, expect } from 'vitest'; // or 'jest'
import { add, divide } from '@/utils/calculator';
describe('Calculator Utils', () => {
describe('add', () => {
it('should add two positive numbers', () => {
expect(add(2, 3)).toBe(5);
});
it('should handle negative numbers', () => {
expect(add(-1, 5)).toBe(4);
});
it('should handle zero', () => {
expect(add(0, 5)).toBe(5);
});
});
describe('divide', () => {
it('should divide two numbers', () => {
expect(divide(10, 2)).toBe(5);
});
it('should handle decimal results', () => {
expect(divide(5, 2)).toBe(2.5);
});
it('should throw on division by zero', () => {
expect(() => divide(10, 0)).toThrow('Division by zero');
});
});
});
Source file:
# src/services/user_service.py
class UserService:
def __init__(self, repository):
self.repository = repository
def get_user(self, user_id: int):
user = self.repository.find_by_id(user_id)
if not user:
raise ValueError(f"User {user_id} not found")
return user
def create_user(self, name: str, email: str):
if not email or '@' not in email:
raise ValueError("Invalid email")
return self.repository.create(name=name, email=email)
Generated test:
# tests/unit/services/test_user_service.py
import pytest
from unittest.mock import Mock
from src.services.user_service import UserService
class TestUserService:
@pytest.fixture
def mock_repository(self):
return Mock()
@pytest.fixture
def service(self, mock_repository):
return UserService(mock_repository)
class TestGetUser:
def test_returns_user_when_found(self, service, mock_repository):
mock_repository.find_by_id.return_value = {"id": 1, "name": "John"}
result = service.get_user(1)
assert result == {"id": 1, "name": "John"}
mock_repository.find_by_id.assert_called_once_with(1)
def test_raises_when_user_not_found(self, service, mock_repository):
mock_repository.find_by_id.return_value = None
with pytest.raises(ValueError, match="User 1 not found"):
service.get_user(1)
class TestCreateUser:
def test_creates_user_with_valid_data(self, service, mock_repository):
mock_repository.create.return_value = {"id": 1, "name": "John"}
result = service.create_user("John", "john@example.com")
mock_repository.create.assert_called_once_with(
name="John",
email="john@example.com"
)
def test_raises_on_invalid_email(self, service):
with pytest.raises(ValueError, match="Invalid email"):
service.create_user("John", "invalid")
def test_raises_on_empty_email(self, service):
with pytest.raises(ValueError, match="Invalid email"):
service.create_user("John", "")
// tests/integration/api/users.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import request from 'supertest';
import { app } from '@/app';
import { db } from '@/database';
describe('Users API', () => {
beforeAll(async () => {
await db.migrate.latest();
await db.seed.run();
});
afterAll(async () => {
await db.destroy();
});
describe('GET /api/users', () => {
it('should return list of users', async () => {
const response = await request(app)
.get('/api/users')
.expect(200);
expect(response.body).toHaveProperty('users');
expect(Array.isArray(response.body.users)).toBe(true);
});
it('should support pagination', async () => {
const response = await request(app)
.get('/api/users?page=1&limit=10')
.expect(200);
expect(response.body.users.length).toBeLessThanOrEqual(10);
expect(response.body).toHaveProperty('pagination');
});
});
describe('POST /api/users', () => {
it('should create new user with valid data', async () => {
const response = await request(app)
.post('/api/users')
.send({
name: 'Test User',
email: 'test@example.com'
})
.expect(201);
expect(response.body).toHaveProperty('id');
expect(response.body.name).toBe('Test User');
});
it('should return 400 for invalid email', async () => {
const response = await request(app)
.post('/api/users')
.send({
name: 'Test User',
email: 'invalid'
})
.expect(400);
expect(response.body).toHaveProperty('error');
});
});
});
## Recorded Actions: User Login
### Steps
1. **navigate**: /login
2. **fill**: input[name="email"] = "user@example.com"
3. **fill**: input[type="password"] = "password123"
4. **click**: button[type="submit"]
5. **wait**: .dashboard-header
6. **assert**: URL contains "/dashboard"
7. **screenshot**: dashboard-loaded
// tests/e2e/auth/login.spec.ts
import { test, expect } from '@playwright/test';
test.describe('User Login', () => {
test('should login with valid credentials', async ({ page }) => {
// Step 1: Navigate to login page
await page.goto('/login');
// Step 2-3: Fill login form
await page.fill('input[name="email"]', 'user@example.com');
await page.fill('input[type="password"]', 'password123');
// Step 4: Submit form
await page.click('button[type="submit"]');
// Step 5: Wait for dashboard
await page.waitForSelector('.dashboard-header');
// Step 6: Verify redirect
await expect(page).toHaveURL(/.*dashboard/);
// Step 7: Capture success state
await page.screenshot({ path: 'test-results/dashboard-loaded.png' });
});
test('should show error for invalid credentials', async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="email"]', 'wrong@example.com');
await page.fill('input[type="password"]', 'wrongpassword');
await page.click('button[type="submit"]');
await expect(page.locator('.error-message')).toBeVisible();
await expect(page).toHaveURL('/login');
});
test('should require email and password', async ({ page }) => {
await page.goto('/login');
await page.click('button[type="submit"]');
await expect(page.locator('input[name="email"]:invalid')).toBeVisible();
});
});
// tests/e2e/pages/LoginPage.ts
import { type Page, type Locator, expect } from '@playwright/test';
export class LoginPage {
readonly page: Page;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
readonly errorMessage: Locator;
readonly forgotPasswordLink: Locator;
constructor(page: Page) {
this.page = page;
this.emailInput = page.locator('input[name="email"]');
this.passwordInput = page.locator('input[type="password"]');
this.submitButton = page.locator('button[type="submit"]');
this.errorMessage = page.locator('.error-message');
this.forgotPasswordLink = page.locator('a[href="/forgot-password"]');
}
async goto() {
await this.page.goto('/login');
}
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
async expectError(message: string) {
await expect(this.errorMessage).toBeVisible();
await expect(this.errorMessage).toContainText(message);
}
async expectLoggedIn() {
await expect(this.page).toHaveURL(/.*dashboard/);
}
}
// tests/e2e/auth/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
test.describe('User Login', () => {
let loginPage: LoginPage;
test.beforeEach(async ({ page }) => {
loginPage = new LoginPage(page);
await loginPage.goto();
});
test('should login with valid credentials', async () => {
await loginPage.login('user@example.com', 'password123');
await loginPage.expectLoggedIn();
});
test('should show error for invalid credentials', async () => {
await loginPage.login('wrong@example.com', 'wrongpassword');
await loginPage.expectError('Invalid credentials');
});
});
// tests/factories/userFactory.ts
import { faker } from '@faker-js/faker';
interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user';
createdAt: Date;
}
export function createUser(overrides: Partial<User> = {}): User {
return {
id: faker.number.int({ min: 1, max: 10000 }),
name: faker.person.fullName(),
email: faker.internet.email(),
role: 'user',
createdAt: faker.date.past(),
...overrides
};
}
export function createUsers(count: number, overrides: Partial<User> = {}): User[] {
return Array.from({ length: count }, () => createUser(overrides));
}
export function createAdmin(overrides: Partial<User> = {}): User {
return createUser({ role: 'admin', ...overrides });
}
# Test Generation Report
## Summary
- **Tests Generated**: 12
- **Test Type**: [Unit/Integration/E2E]
- **Framework**: [Jest/Vitest/pytest/Playwright]
- **Files Created**: 3
## Generated Files
### 1. tests/unit/services/user.test.ts
- Tests: 6
- Coverage: UserService class
- Mocks: UserRepository
### 2. tests/e2e/auth/login.spec.ts
- Tests: 4
- Scenarios: Valid login, invalid credentials, validation
- Page Objects: LoginPage
### 3. tests/factories/userFactory.ts
- Utilities: createUser, createUsers, createAdmin
## Test Coverage Targets
- [ ] Happy path scenarios
- [ ] Error handling
- [ ] Edge cases
- [ ] Input validation
- [ ] Async operations
## Next Steps
1. Run tests with `test-runner` agent
2. Add additional edge cases if needed
3. Update tests as requirements change
Designs feature architectures by analyzing existing codebase patterns and conventions, then providing comprehensive implementation blueprints with specific files to create/modify, component designs, data flows, and build sequences