4.5 KiB
4.5 KiB
Testing Guide
This document explains how to run and write tests for Brackt.com.
Overview
We use two testing frameworks:
- Vitest + React Testing Library - Unit and component tests
- Cypress - End-to-end integration tests
Running Tests
Unit Tests
# Run tests in watch mode (interactive)
npm test
# Run tests once (CI mode)
npm run test:run
# Run tests with UI
npm run test:ui
# Run tests with coverage
npm run test:coverage
E2E Tests
# Open Cypress interactive mode
npm run test:e2e
# Run Cypress headless (CI mode)
npm run test:e2e:headless
# Run all tests (unit + E2E)
npm run test:all
Writing Tests
Unit Tests
Unit tests go in __tests__ directories next to the code they test:
app/
lib/
utils.ts
__tests__/
utils.test.ts
models/
league.ts
__tests__/
league.test.ts
Example unit test:
import { describe, it, expect } from 'vitest';
import { myFunction } from '../myFunction';
describe('myFunction', () => {
it('should do something', () => {
const result = myFunction('input');
expect(result).toBe('expected output');
});
});
Component Tests
Component tests also go in __tests__ directories:
app/
components/
DraftGrid.tsx
__tests__/
DraftGrid.test.tsx
Example component test:
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { MyComponent } from '../MyComponent';
describe('MyComponent', () => {
it('should render correctly', () => {
render(<MyComponent title="Test" />);
expect(screen.getByText('Test')).toBeInTheDocument();
});
});
E2E Tests
E2E tests go in the cypress/e2e directory:
cypress/
e2e/
league-creation.cy.ts
league-settings.cy.ts
draft-room.cy.ts
Example E2E test:
describe('Feature Name', () => {
it('should do something', () => {
cy.visit('/some-page');
cy.get('button').click();
cy.findByText('Expected Text').should('exist');
});
});
Test Fixtures
Reusable test data is stored in app/test/fixtures/:
// app/test/fixtures/league.ts
export const mockLeague = {
id: 'league-1',
name: 'Test League',
// ...
};
Import and use in tests:
import { mockLeague } from '~/test/fixtures/league';
CI/CD Integration
Tests run automatically on:
- Every push to
mainordevelop - Every pull request
GitHub Actions Workflow
- Test Job - Runs unit tests with PostgreSQL service
- Typecheck Job - Runs TypeScript type checking
- Build Job - Builds Docker image (only on main branch)
- Deploy Job - Deploys to server (only after all tests pass)
Tests must pass before deployment!
Coverage
Coverage reports are generated automatically:
- HTML report:
coverage/index.html - JSON report:
coverage/coverage-final.json
View coverage locally:
npm run test:coverage
open coverage/index.html
Best Practices
Unit Tests
- Test one thing per test
- Use descriptive test names
- Mock external dependencies
- Aim for 80%+ coverage
Component Tests
- Test user interactions, not implementation
- Use
screen.getByRole()when possible - Test accessibility
- Keep tests focused
E2E Tests
- Test critical user flows
- Use
data-testidsparingly - Prefer semantic queries
- Keep tests independent
Debugging Tests
Vitest
# Run specific test file
npm test -- path/to/test.test.ts
# Run tests matching pattern
npm test -- --grep "pattern"
# Debug in VS Code
# Add breakpoint and use "Debug Test" in test file
Cypress
# Open Cypress UI for debugging
npm run test:e2e
# Run specific test file
npx cypress run --spec "cypress/e2e/specific-test.cy.ts"
Common Issues
Tests fail locally but pass in CI
- Check Node version matches CI (v20)
- Ensure database is running locally
- Check environment variables
Cypress tests timeout
- Increase timeout in
cypress.config.ts - Check if app is running on correct port
- Verify selectors are correct
Coverage not generating
- Ensure
@vitest/coverage-v8is installed - Check
vitest.config.tscoverage settings - Run with
npm run test:coverage