testing
Testing
A comprehensive testing skill that helps design, implement, and maintain effective test suites across different testing levels and frameworks.
Quick Start
Basic testing workflow:
# Identify testable components
# Design test cases
# Implement tests following patterns
# Run tests and verify coverage
# Maintain and refactor tests
Core Capabilities
1. Test Strategy Design
Plan comprehensive testing approaches:
- Test Pyramid: Balance unit, integration, and E2E tests
- Test Coverage: Identify what needs testing
- Test Data: Design test fixtures and mocks
- Test Environments: Configure test infrastructure
- CI/CD Integration: Automate test execution
2. Unit Testing
Test individual components in isolation:
- Function Tests: Test pure functions and methods
- Class Tests: Test class behavior and state
- Mock Dependencies: Isolate units from dependencies
- Edge Cases: Test boundary conditions
- Error Handling: Test exception paths
3. Integration Testing
Test component interactions:
- API Testing: Test HTTP endpoints and responses
- Database Testing: Test data persistence and queries
- Service Integration: Test service-to-service communication
- External Dependencies: Test third-party integrations
- Contract Testing: Verify API contracts
4. Test-Driven Development (TDD)
Follow the TDD red-green-refactor cycle:
- Red: Write failing test first
- Green: Write minimal code to pass
- Refactor: Improve code while keeping tests green
5. Test Quality
Ensure high-quality tests:
- Readability: Clear test names and structure
- Maintainability: DRY principles, shared fixtures
- Performance: Fast test execution
- Reliability: Deterministic, no flaky tests
- Independence: Tests don't depend on each other
Testing Patterns
AAA Pattern (Arrange-Act-Assert)
def test_calculate_total_with_tax():
# Arrange
items = [
{'name': 'Item 1', 'price': 10.0},
{'name': 'Item 2', 'price': 20.0}
]
tax_rate = 0.1
# Act
result = calculate_total(items, tax_rate)
# Assert
assert result == 33.0
Given-When-Then (BDD Style)
def test_user_registration():
# Given a new user with valid credentials
user_data = {
'email': 'test@example.com',
'password': 'SecurePass123!',
'name': 'Test User'
}
# When registering the user
response = register_user(user_data)
# Then user is created and confirmation email is sent
assert response.status_code == 201
assert response.data['id'] is not None
assert mock_email_service.send.called
Test Fixtures
import pytest
@pytest.fixture
def sample_user():
"""Provide a sample user for testing."""
return {
'id': 1,
'email': 'test@example.com',
'name': 'Test User'
}
@pytest.fixture
def database_session():
"""Provide a clean database session for each test."""
session = create_test_session()
yield session
session.rollback()
session.close()
def test_create_user(database_session, sample_user):
# Use fixtures in test
user = User(**sample_user)
database_session.add(user)
database_session.commit()
assert user.id is not None
Testing by Framework
React + TypeScript (Jest + React Testing Library)
- Component Testing: Renderizado, interacciones, estados
- Hook Testing: Custom hooks con renderHook
- Event Testing: Clicks, inputs, formularios
- Async Testing: waitFor, findBy queries
- Mock Testing: jest.fn(), jest.mock()
Angular (Jasmine/Karma)
- Component Testing: TestBed, fixtures, change detection
- Service Testing: HttpClientTestingModule, mock HttpClient
- Dependency Injection: TestBed.inject()
- Async Testing: fakeAsync, tick, flush
- Integration Testing: RouterTestingModule, componentes integrados
Python (pytest + FastAPI)
- Unit Testing: pytest fixtures, parametrize
- API Testing: TestClient, async testing
- Mock Testing: unittest.mock, pytest-mock
- Database Testing: fixtures con rollback
- Integration Testing: test containers, real databases
Java + Spring Boot (JUnit 5 + Mockito)
- Unit Testing: @ExtendWith, @Mock, @InjectMocks
- Service Testing: Mockito mocks, verify, when
- Integration Testing: @SpringBootTest, @AutoConfigureTestDatabase
- REST API Testing: MockMvc, TestRestTemplate
- Transaction Testing: @Transactional en tests
Kotlin (JUnit 5 + MockK + Coroutines)
- Coroutine Testing: runTest, runBlocking
- Mock Testing: mockk, coEvery, coVerify
- Flow Testing: turbine, toList()
- Suspend Function Testing: TestCoroutineDispatcher
- Integration Testing: Spring Boot con coroutines
Nota: Para ejemplos completos específicos de tu stack tecnológico (React, Angular, Python, Java, Kotlin), consulta EXAMPLES_STACK.md.
Ejemplos Genéricos (Legacy)
Python (pytest)
import { UserService } from './user-service';
import { EmailService } from './email-service';
// Mock the email service
jest.mock('./email-service');
describe('UserService', () => {
let userService;
let emailService;
beforeEach(() => {
// Reset mocks before each test
jest.clearAllMocks();
// Create instances
emailService = new EmailService();
userService = new UserService(emailService);
});
describe('createUser', () => {
it('should create user and send welcome email', async () => {
// Arrange
const userData = {
email: 'test@example.com',
name: 'Test User'
};
// Act
const user = await userService.createUser(userData);
// Assert
expect(user.email).toBe('test@example.com');
expect(user.name).toBe('Test User');
expect(emailService.sendWelcome).toHaveBeenCalledWith(user);
expect(emailService.sendWelcome).toHaveBeenCalledTimes(1);
});
it('should throw error for duplicate email', async () => {
// Arrange
const userData = { email: 'existing@example.com', name: 'User' };
// Act & Assert
await expect(userService.createUser(userData))
.rejects
.toThrow('Email already exists');
});
it.each([
['not-an-email'],
['@example.com'],
['test@'],
[''],
])('should throw error for invalid email: %s', async (invalidEmail) => {
const userData = { email: invalidEmail, name: 'User' };
await expect(userService.createUser(userData))
.rejects
.toThrow('Invalid email format');
});
});
describe('getUserById', () => {
it('should return user when found', async () => {
const userId = '123';
const expectedUser = { id: userId, name: 'Test User' };
const user = await userService.getUserById(userId);
expect(user).toEqual(expectedUser);
});
it('should return null when user not found', async () => {
const userId = 'nonexistent';
const user = await userService.getUserById(userId);
expect(user).toBeNull();
});
});
});
Java (JUnit 5)
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class UserServiceTest {
@Mock
private EmailService emailService;
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
@DisplayName("Should create user and send welcome email")
void testCreateUserSuccess() {
// Arrange
UserDTO userData = new UserDTO("test@example.com", "Test User");
User expectedUser = new User(1L, "test@example.com", "Test User");
when(userRepository.existsByEmail(userData.getEmail()))
.thenReturn(false);
when(userRepository.save(any(User.class)))
.thenReturn(expectedUser);
// Act
User result = userService.createUser(userData);
// Assert
assertNotNull(result);
assertEquals("test@example.com", result.getEmail());
assertEquals("Test User", result.getName());
verify(userRepository).existsByEmail("test@example.com");
verify(userRepository).save(any(User.class));
verify(emailService).sendWelcome(expectedUser);
}
@Test
@DisplayName("Should throw exception for duplicate email")
void testCreateUserDuplicateEmail() {
// Arrange
UserDTO userData = new UserDTO("existing@example.com", "User");
when(userRepository.existsByEmail(userData.getEmail()))
.thenReturn(true);
// Act & Assert
assertThrows(DuplicateEmailException.class, () -> {
userService.createUser(userData);
});
verify(userRepository).existsByEmail("existing@example.com");
verify(userRepository, never()).save(any(User.class));
verify(emailService, never()).sendWelcome(any(User.class));
}
@ParameterizedTest
@ValueSource(strings = {"not-an-email", "@example.com", "test@", ""})
@DisplayName("Should throw exception for invalid email")
void testCreateUserInvalidEmail(String invalidEmail) {
UserDTO userData = new UserDTO(invalidEmail, "User");
assertThrows(InvalidEmailException.class, () -> {
userService.createUser(userData);
});
}
@Nested
@DisplayName("GetUserById tests")
class GetUserByIdTests {
@Test
@DisplayName("Should return user when found")
void testGetUserByIdFound() {
Long userId = 1L;
User expectedUser = new User(userId, "test@example.com", "Test User");
when(userRepository.findById(userId))
.thenReturn(Optional.of(expectedUser));
Optional<User> result = userService.getUserById(userId);
assertTrue(result.isPresent());
assertEquals(expectedUser, result.get());
}
@Test
@DisplayName("Should return empty when user not found")
void testGetUserByIdNotFound() {
Long userId = 999L;
when(userRepository.findById(userId))
.thenReturn(Optional.empty());
Optional<User> result = userService.getUserById(userId);
assertFalse(result.isPresent());
}
}
}
Integration Testing
API Integration Test (Python)
import pytest
from fastapi.testclient import TestClient
from myapp import app, database
@pytest.fixture(scope="module")
def test_client():
"""Create a test client for the API."""
client = TestClient(app)
yield client
@pytest.fixture(scope="function")
def clean_database():
"""Provide a clean database for each test."""
database.create_all()
yield
database.drop_all()
class TestUserAPI:
"""Integration tests for User API endpoints."""
def test_create_user_endpoint(self, test_client, clean_database):
"""Test POST /users creates a new user."""
# Arrange
user_data = {
"email": "test@example.com",
"name": "Test User",
"password": "SecurePass123!"
}
# Act
response = test_client.post("/users", json=user_data)
# Assert
assert response.status_code == 201
data = response.json()
assert data["email"] == "test@example.com"
assert data["name"] == "Test User"
assert "id" in data
assert "password" not in data # Password should not be returned
def test_get_user_endpoint(self, test_client, clean_database):
"""Test GET /users/{id} retrieves user."""
# Arrange - create a user first
user_data = {"email": "test@example.com", "name": "Test User"}
create_response = test_client.post("/users", json=user_data)
user_id = create_response.json()["id"]
# Act
response = test_client.get(f"/users/{user_id}")
# Assert
assert response.status_code == 200
data = response.json()
assert data["id"] == user_id
assert data["email"] == "test@example.com"
def test_list_users_pagination(self, test_client, clean_database):
"""Test GET /users with pagination."""
# Arrange - create multiple users
for i in range(15):
test_client.post("/users", json={
"email": f"user{i}@example.com",
"name": f"User {i}"
})
# Act
response = test_client.get("/users?page=1&per_page=10")
# Assert
assert response.status_code == 200
data = response.json()
assert len(data["items"]) == 10
assert data["total"] == 15
assert data["page"] == 1
Test Coverage
Aim for comprehensive coverage:
# Generate coverage report
pytest --cov=myapp --cov-report=html tests/
# Coverage goals
# - Overall: > 80%
# - Critical paths: 100%
# - New code: 100%
Focus coverage on:
- Business logic
- Error handling
- Edge cases
- Security-critical code
Best Practices
- Test Naming: Use descriptive names that explain what's being tested
- One Assertion Per Test: Focus each test on one behavior
- Test Independence: Tests should not depend on execution order
- Fast Tests: Keep unit tests under 100ms
- Avoid Logic in Tests: Tests should be simple and obvious
- Use Fixtures: Share setup code via fixtures
- Mock External Dependencies: Isolate unit tests
- Test Error Cases: Don't just test the happy path
- Keep Tests Maintainable: Apply same quality standards as production code
- Test Behavior, Not Implementation: Test what, not how
When to Use This Skill
Use this skill when:
- Writing new tests for existing code
- Implementing TDD for new features
- Improving test coverage
- Designing test strategies
- Setting up testing infrastructure
- Debugging failing tests
- Refactoring test code
- Creating test fixtures and mocks
- Implementing integration tests
- Setting up CI/CD test pipelines
Examples
See EXAMPLES.md for complete testing examples across different scenarios and frameworks.
For test templates, see templates/.
For test utilities and helpers, see scripts/test_helpers.py.
More from josavicentevw/ai-agent-skills
human-resources
Human Resources skill for recruiting, onboarding, employee engagement, policies, performance management, and HR operations. Use when handling HR processes, recruiting, employee relations, or organizational development.
55ux-ui-design
UX/UI Design skill for user research, wireframing, prototyping, visual design, interaction design, usability testing, accessibility, design systems, and responsive design. Use when designing user interfaces, conducting user research, creating design systems, evaluating usability, or when user mentions UX, UI, wireframes, prototypes, user flows, or accessibility.
20architecture
Design, evaluate, and document software architectures including system design, design patterns, architecture patterns, scalability planning, and technology selection. Use when designing systems, choosing architectures, evaluating design decisions, or when user mentions architecture, system design, or scalability.
2