"It works on my machine" is not acceptable for tests. A test should produce identical results whether it's running on your laptop, your colleague's Docker container, or GitHub Actions.
// 😱 BAD: Absolute paths that only exist on one machine
test('loads config file', () => {
const config = loadConfig('/Users/alice/project/config.json');
expect(config.apiUrl).toBe('https://api.example.com');
});
test('saves to temp directory', () => {
const file = saveTemp('/tmp/test-output.txt', data);
// Fails on Windows: no /tmp directory!
expect(fs.existsSync('/tmp/test-output.txt')).toBe(true);
});
// ✅ GOOD: Use relative paths and cross-platform abstractions
test('loads config file', () => {
const config = loadConfig(path.join(__dirname, 'fixtures', 'config.json'));
expect(config.apiUrl).toBe('https://api.example.com');
});
test('saves to temp directory', () => {
const tempDir = os.tmpdir(); // Works on all platforms
const tempFile = path.join(tempDir, 'test-output.txt');
saveTemp(tempFile, data);
expect(fs.existsSync(tempFile)).toBe(true);
});// 😱 BAD: Assumes specific tools or services are running
test('processes image', () => {
// Assumes ImageMagick is installed
exec('convert input.jpg -resize 100x100 output.jpg');
// Assumes Redis is running on default port
const redis = new Redis({ host: 'localhost', port: 6379 });
redis.set('key', 'value');
});
test('sends email', () => {
// Assumes SMTP server on localhost
sendEmail({
host: 'localhost',
port: 25,
to: 'test@example.com'
});
});
// ✅ GOOD: Mock or containerize external dependencies
test('processes image', () => {
// Use a JS library instead of system dependency
const sharp = require('sharp');
await sharp('input.jpg').resize(100, 100).toFile('output.jpg');
});
test('caching works', () => {
// Use in-memory mock or test container
const cache = new Map(); // or use testcontainers for real Redis
cache.set('key', 'value');
expect(cache.get('key')).toBe('value');
});// 😱 BAD: Tests fail in different timezones or at different times
test('formats date correctly', () => {
const date = new Date('2024-01-15 10:00:00');
expect(formatDate(date)).toBe('Jan 15, 10:00 AM');
// Fails in different timezone!
});
test('business hours check', () => {
const now = new Date();
// Fails when run at night or weekends!
expect(isBusinessHours(now)).toBe(true);
});
// ✅ GOOD: Control time and timezone in tests
test('formats date correctly', () => {
// Use fixed timezone
const date = new Date('2024-01-15T10:00:00Z');
expect(formatDate(date, 'UTC')).toBe('Jan 15, 10:00 AM');
});
test('business hours check', () => {
// Mock the current time
const mockDate = new Date('2024-01-15T14:00:00Z'); // Monday 2 PM
jest.spyOn(Date, 'now').mockReturnValue(mockDate.getTime());
expect(isBusinessHours()).toBe(true);
const weekend = new Date('2024-01-14T14:00:00Z'); // Sunday
Date.now.mockReturnValue(weekend.getTime());
expect(isBusinessHours()).toBe(false);
});// 😱 BAD: Tests fail when offline or API is down
test('fetches user data', async () => {
const user = await fetch('https://api.github.com/users/octocat');
expect(user.login).toBe('octocat');
// Fails when: offline, API down, rate limited, response changes
});
test('geocoding works', async () => {
const coords = await geocode('New York');
expect(coords.lat).toBeCloseTo(40.7128);
// Fails without internet or API key
});
// ✅ GOOD: Mock external APIs
test('fetches user data', async () => {
// Mock the API call
fetchMock.get('https://api.github.com/users/octocat', {
login: 'octocat',
id: 583231,
name: 'The Octocat'
});
const user = await fetchGitHubUser('octocat');
expect(user.login).toBe('octocat');
});
// Or use recorded fixtures (like VCR)
test('geocoding works', async () => {
// Use recorded response
const coords = await geocodeWithFixture('New York');
expect(coords.lat).toBeCloseTo(40.7128);
});// Use testcontainers for real services
import { GenericContainer } from 'testcontainers';
beforeAll(async () => {
const redis = await new GenericContainer('redis')
.withExposedPorts(6379)
.start();
process.env.REDIS_URL = `redis://${redis.getHost()}:${redis.getMappedPort(6379)}`;
});// .env.test
DATABASE_URL=sqlite::memory:
API_KEY=test-key-123
TEMP_DIR=./test-temp
// test setup
require('dotenv').config({ path: '.env.test' });
// Now tests work everywhere with same config// Platform abstraction
class FileSystem {
static getTempDir() {
return process.platform === 'win32'
? process.env.TEMP
: '/tmp';
}
static getPathSeparator() {
return path.sep; // Handles / vs \
}
static normalize(filepath) {
return path.normalize(filepath);
}
}// package.json
{
"scripts": {
"test": "jest",
"test:ci": "jest --ci --coverage",
"test:setup": "docker-compose up -d test-deps"
},
"engines": {
"node": ">=18.0.0"
}
}
// README.md
## Running Tests
```bash
# Install dependencies
npm install
# Start test containers (PostgreSQL, Redis)
npm run test:setup
# Run tests
npm test
```# GitHub Actions example
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [18, 20, 22]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test