Testing & QA Issues
The following examples demonstrate common testing and QA problems:
1. No Tests at All Missing
Codebase with zero test coverage:
// VIOLATION: No tests
// src/userService.js
function createUser(userData) {
// Complex business logic
return database.save(userData);
}
// No test files exist
// No test directory
// No test framework installed
// Code coverage: 0%
Problem: 0% Coverage - No tests means bugs go undetected
No way to verify code works or catch regressions
2. Poor Test Coverage Coverage
Tests only cover happy paths:
// VIOLATION: Poor coverage
describe('UserService', () => {
it('should create user', () => {
const user = createUser({ name: 'John', email: 'john@test.com' });
expect(user).toBeDefined();
});
});
// Missing tests for:
// - Invalid email
// - Missing required fields
// - Database errors
// - Duplicate users
// - Edge cases
// Coverage: 15%
Problem: 15% Coverage - Only happy path tested
Edge cases and error conditions not covered
3. Flaky Tests Flaky
Tests that randomly pass or fail:
// VIOLATION: Flaky tests
describe('UserService', () => {
it('should process user within 1 second', () => {
const start = Date.now();
processUser(userData);
const duration = Date.now() - start;
expect(duration).toBeLessThan(1000); // Fails randomly
});
it('should generate unique ID', () => {
const id1 = generateId();
const id2 = generateId();
expect(id1).not.toBe(id2); // May fail if timing is same
});
it('should fetch user data', async () => {
const user = await fetchUser(); // Depends on network timing
expect(user).toBeDefined();
});
});
// Tests fail randomly due to timing, network, or race conditions
Problem: Timing-dependent, Network-dependent, Race conditions
Tests unreliable, developers ignore failures
4. No Test Automation Automation
Tests must be run manually:
# VIOLATION: No automation
# Tests must be run manually
# No CI/CD integration
# No automated test runs
# Developers forget to run tests
# Tests run inconsistently
# Manual process:
1. Open terminal
2. Navigate to project
3. Run: npm test
4. Check results manually
5. Report findings in Slack
# No automation, no consistency
Problem: Manual execution, No CI/CD, Inconsistent
Tests often skipped, results unreliable
5. Hardcoded Test Data Data
Test data hardcoded in tests:
// VIOLATION: Hardcoded test data
describe('PaymentService', () => {
it('should process payment', () => {
const payment = {
amount: 100.00,
cardNumber: '4111111111111111',
cvv: '123',
expiry: '12/25',
userId: 42
};
// Hardcoded values
// Tests break if data changes
// Can't test different scenarios easily
});
it('should handle invalid card', () => {
const payment = {
amount: 100.00,
cardNumber: '4111111111111111', // Same data reused
cvv: '123',
expiry: '12/25'
};
});
});
// Hard to maintain, can't test edge cases
Problem: Hardcoded values, Not reusable, Brittle
Tests break when data requirements change
6. Tests That Don't Test Anything Useless
Tests that always pass:
// VIOLATION: Useless tests
describe('UserService', () => {
it('should do something', () => {
// No assertions
createUser({ name: 'John' });
});
it('should work', () => {
expect(true).toBe(true); // Always passes
});
it('should not crash', () => {
try {
processUser(null);
} catch (e) {
// Swallow error, test passes
}
});
});
// Tests don't verify anything
Problem: No assertions, False confidence
Tests pass but don't verify functionality
7. No Integration Tests Integration
Only unit tests, no integration testing:
// VIOLATION: No integration tests
// Only unit tests exist
describe('UserService', () => {
it('should create user', () => {
// Mocked database
const mockDb = jest.fn();
// Mocked email service
const mockEmail = jest.fn();
// No real integration testing
});
});
// Missing:
// - API endpoint tests
// - Database integration tests
// - Service integration tests
// - End-to-end tests
// Components work in isolation but not together
Problem: No integration, Components tested in isolation
System may fail when components interact
8. Poor Test Organization Organization
Tests scattered and disorganized:
# VIOLATION: Poor organization
project/
src/
user.js
order.js
test.js # All tests in one file
test2.js # More tests
tests.js # Even more tests
user.test.js # Some tests here
order.test.js # Some tests there
__tests__/
old-tests.js # Old tests
new-tests.js # New tests
temp.js # Temporary tests
# No clear structure, hard to find tests
Problem: Scattered files, No structure, Hard to maintain
Difficult to find and maintain tests
9. No Test Documentation Documentation
Tests without clear descriptions:
// VIOLATION: No documentation
describe('UserService', () => {
it('test1', () => {
// What does this test?
});
it('test2', () => {
// Unclear purpose
});
it('works', () => {
// What works?
});
});
// Test names don't explain what they test
Problem: Unclear names, No documentation
Can't understand what tests verify
10. Tests Depend on External Services Dependencies
Tests require live external APIs:
// VIOLATION: External dependencies
describe('PaymentService', () => {
it('should process payment', async () => {
// Calls real payment API
const result = await paymentAPI.charge({
amount: 100,
card: '4111111111111111'
});
// Test fails if API is down
// Test charges real money
// Test is slow
});
it('should send email', async () => {
// Sends real email
await emailService.send('test@example.com', 'Test');
// Spam emails sent
// Test depends on email service
});
});
// Tests unreliable, slow, may have side effects
Problem: External dependencies, Slow, Side effects
Tests fail when external services are unavailable
11. No Performance Tests Performance
No testing of performance or load:
// VIOLATION: No performance tests
describe('UserService', () => {
it('should create user', () => {
// Only tests functionality
// No performance requirements
// No load testing
// No stress testing
});
});
// Missing:
// - Response time tests
// - Throughput tests
// - Load tests
// - Stress tests
// - Memory leak tests
// Performance issues go undetected
Problem: No performance testing, No load testing
Performance issues discovered in production
12. No Accessibility Tests Accessibility
No testing for accessibility:
// VIOLATION: No accessibility tests
describe('LoginForm', () => {
it('should submit form', () => {
// Only tests functionality
// No accessibility checks
});
});
// Missing:
// - Screen reader tests
// - Keyboard navigation tests
// - Color contrast tests
// - ARIA attribute tests
// - WCAG compliance tests
// Accessibility issues go undetected
Problem: No a11y tests, WCAG violations
Accessibility issues not caught before release
13. No Security Tests Security
No testing for security vulnerabilities:
// VIOLATION: No security tests
describe('UserService', () => {
it('should create user', () => {
// Only tests happy path
// No security testing
});
});
// Missing:
// - SQL injection tests
// - XSS tests
// - CSRF tests
// - Authentication tests
// - Authorization tests
// - Input validation tests
// Security vulnerabilities go undetected
Problem: No security tests, Vulnerabilities undetected
Security issues discovered by attackers
14. Tests Not Maintained Maintenance
Outdated tests that don't match code:
// VIOLATION: Outdated tests
describe('UserService', () => {
it('should create user', () => {
// Old API, code has changed
const user = createUser('John', 'john@test.com');
// New API requires object: createUser({ name, email })
// Test fails but not updated
});
it('should validate email', () => {
// Old validation rules
expect(validateEmail('test@test')).toBe(true);
// New rules require .com, test outdated
});
});
// Tests broken but not fixed
Problem: Outdated, Broken, Not maintained
Tests don't reflect current code behavior
15. No Test Metrics or Reporting Metrics
No visibility into test results:
# VIOLATION: No metrics
# Tests run but:
# - No coverage reports
# - No test duration tracking
# - No failure rate tracking
# - No trend analysis
# - No test quality metrics
# Can't answer:
# - What's our test coverage?
# - Which tests are slow?
# - Which tests fail most often?
# - Are we improving?
# No visibility into test quality
Problem: No metrics, No reporting, No visibility
Can't measure or improve test quality
16. Tests Written After Code TDD
Tests written after implementation:
# VIOLATION: Tests after code
1. Write feature code
2. Test manually in browser
3. Deploy to production
4. Write tests later (maybe)
5. Tests written to match existing code
6. Tests don't catch design issues
# Problems:
# - Tests verify implementation, not requirements
# - Design issues not caught
# - Code written without testability in mind
# - Tests harder to write
Problem: No TDD, Tests verify implementation
Tests don't drive design or catch issues early
Testing & QA Best Practices
The following examples demonstrate proper testing and QA practices:
1. Comprehensive Test Coverage Coverage
// Compliant: Comprehensive tests
describe('UserService', () => {
it('should create user with valid data', () => {
const user = createUser({ name: 'John', email: 'john@test.com' });
expect(user).toBeDefined();
expect(user.id).toBeDefined();
});
it('should reject invalid email', () => {
expect(() => {
createUser({ name: 'John', email: 'invalid' });
}).toThrow('Invalid email');
});
it('should reject missing required fields', () => {
expect(() => {
createUser({ name: 'John' });
}).toThrow('Email is required');
});
it('should handle database errors', async () => {
mockDatabase.save.mockRejectedValue(new Error('DB Error'));
await expect(createUser({ name: 'John', email: 'john@test.com' }))
.rejects.toThrow('Failed to save user');
});
it('should prevent duplicate emails', () => {
createUser({ name: 'John', email: 'john@test.com' });
expect(() => {
createUser({ name: 'Jane', email: 'john@test.com' });
}).toThrow('Email already exists');
});
});
// Coverage: 85%+, tests all paths
✓ Benefits: 85%+ Coverage - All code paths tested
2. Stable, Reliable Tests Stable
// Compliant: Stable tests
describe('UserService', () => {
beforeEach(() => {
jest.useFakeTimers();
mockDatabase.reset();
});
it('should process user within timeout', () => {
jest.advanceTimersByTime(500);
const result = processUser(userData);
expect(result).toBeDefined();
// No timing issues
});
it('should generate unique ID', () => {
const id1 = generateId();
jest.advanceTimersByTime(1);
const id2 = generateId();
expect(id1).not.toBe(id2);
// Deterministic IDs
});
it('should fetch user data', async () => {
mockFetch.mockResolvedValue({ data: { id: 1 } });
const user = await fetchUser();
expect(user).toBeDefined();
// No network dependency
});
});
// Tests are deterministic and reliable
✓ Benefits: Deterministic, Fast, Reliable
3. Automated Test Execution Automation
# Compliant: CI/CD automation
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- run: npm install
- run: npm test
- run: npm run test:coverage
- uses: codecov/codecov-action@v2
# Tests run automatically on every change
✓ Benefits: Automated, Consistent, CI/CD integrated
4. Test Data Factories Factories
// Compliant: Test data factories
function createPaymentData(overrides = {}) {
return {
amount: 100.00,
cardNumber: '4111111111111111',
cvv: '123',
expiry: '12/25',
userId: 42,
...overrides
};
}
describe('PaymentService', () => {
it('should process valid payment', () => {
const payment = createPaymentData();
expect(processPayment(payment)).toBeDefined();
});
it('should reject invalid card', () => {
const payment = createPaymentData({ cardNumber: 'invalid' });
expect(() => processPayment(payment)).toThrow();
});
it('should handle expired card', () => {
const payment = createPaymentData({ expiry: '01/20' });
expect(() => processPayment(payment)).toThrow('Card expired');
});
});
// Reusable, maintainable test data
✓ Benefits: Reusable, Maintainable, Flexible
5. Meaningful Assertions Assertions
// Compliant: Meaningful assertions
describe('UserService', () => {
it('should create user with all required fields', () => {
const user = createUser({ name: 'John', email: 'john@test.com' });
expect(user).toBeDefined();
expect(user.id).toBeDefined();
expect(user.name).toBe('John');
expect(user.email).toBe('john@test.com');
expect(user.createdAt).toBeInstanceOf(Date);
});
it('should validate email format', () => {
expect(() => {
createUser({ name: 'John', email: 'invalid' });
}).toThrow('Email must be a valid email address');
});
});
// Tests verify specific behavior
✓ Benefits: Specific, Verifiable, Clear
6. Integration Tests Integration
// Compliant: Integration tests
describe('User API Integration', () => {
it('should create and retrieve user', async () => {
// Real database, no mocks
const user = await createUser({ name: 'John', email: 'john@test.com' });
const retrieved = await getUser(user.id);
expect(retrieved.name).toBe('John');
expect(retrieved.email).toBe('john@test.com');
});
it('should send welcome email on user creation', async () => {
const emailSpy = jest.spyOn(emailService, 'send');
await createUser({ name: 'John', email: 'john@test.com' });
expect(emailSpy).toHaveBeenCalledWith(
'john@test.com',
'Welcome',
expect.stringContaining('John')
);
});
});
// Tests verify components work together
✓ Benefits: Real integration, Component interaction
7. Well-Organized Test Structure Structure
# Compliant: Organized structure
project/
src/
user/
userService.js
userService.test.js # Tests next to code
order/
orderService.js
orderService.test.js
__tests__/
integration/
user.integration.test.js
order.integration.test.js
e2e/
user-flow.e2e.test.js
test/
fixtures/
users.js
orders.js
helpers/
testHelpers.js
# Clear structure, easy to find tests
✓ Benefits: Organized, Maintainable, Discoverable
8. Clear Test Documentation Documentation
// Compliant: Clear documentation
describe('UserService', () => {
/**
* Verifies that a user can be created with valid data
* and receives a unique ID and timestamp
*/
it('should create user with valid data and assign unique ID', () => {
const user = createUser({ name: 'John', email: 'john@test.com' });
expect(user.id).toBeDefined();
expect(user.createdAt).toBeInstanceOf(Date);
});
/**
* Ensures email validation rejects invalid formats
* including missing @ symbol, invalid domains, etc.
*/
it('should reject user creation with invalid email format', () => {
expect(() => {
createUser({ name: 'John', email: 'invalid-email' });
}).toThrow('Email must be a valid email address');
});
});
// Test names and comments explain purpose
✓ Benefits: Self-documenting, Clear purpose
9. Mocked External Dependencies Mocks
// Compliant: Mocked dependencies
jest.mock('../services/paymentAPI');
jest.mock('../services/emailService');
describe('PaymentService', () => {
it('should process payment without calling real API', async () => {
paymentAPI.charge.mockResolvedValue({ success: true });
const result = await processPayment({ amount: 100 });
expect(result.success).toBe(true);
expect(paymentAPI.charge).toHaveBeenCalledWith({ amount: 100 });
// No real API call, fast, reliable
});
it('should send email notification', async () => {
emailService.send.mockResolvedValue({ sent: true });
await sendPaymentConfirmation('user@test.com');
expect(emailService.send).toHaveBeenCalled();
// No real email sent
});
});
// Tests are fast, reliable, no side effects
✓ Benefits: Fast, Reliable, No side effects
10. Performance Tests Performance
// Compliant: Performance tests
describe('UserService Performance', () => {
it('should process user within 100ms', async () => {
const start = performance.now();
await createUser({ name: 'John', email: 'john@test.com' });
const duration = performance.now() - start;
expect(duration).toBeLessThan(100);
});
it('should handle 1000 concurrent requests', async () => {
const requests = Array(1000).fill(null).map(() =>
createUser({ name: 'Test', email: `test${Math.random()}@test.com` })
);
const start = performance.now();
await Promise.all(requests);
const duration = performance.now() - start;
expect(duration).toBeLessThan(5000); // 5 seconds for 1000 requests
});
});
// Performance requirements verified
✓ Benefits: Performance verified, Load tested
11. Accessibility Tests Accessibility
// Compliant: Accessibility tests
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
describe('LoginForm Accessibility', () => {
it('should have no accessibility violations', async () => {
const { container } = render();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
it('should be keyboard navigable', () => {
const { getByLabelText } = render();
const emailInput = getByLabelText('Email');
const passwordInput = getByLabelText('Password');
expect(emailInput).toHaveFocus();
fireEvent.keyDown(emailInput, { key: 'Tab' });
expect(passwordInput).toHaveFocus();
});
it('should have proper ARIA labels', () => {
const { getByRole } = render();
expect(getByRole('form')).toHaveAttribute('aria-label', 'Login form');
});
});
// Accessibility verified automatically
✓ Benefits: WCAG compliance, Automated a11y testing
12. Security Tests Security
// Compliant: Security tests
describe('UserService Security', () => {
it('should prevent SQL injection', async () => {
const maliciousInput = "'; DROP TABLE users; --";
await expect(
getUser(maliciousInput)
).rejects.toThrow();
// Database should not execute malicious SQL
});
it('should sanitize XSS in user input', () => {
const xssInput = '';
const user = createUser({ name: xssInput, email: 'test@test.com' });
expect(user.name).not.toContain('