JavaScript Testing Strategies

Testing is essential for maintaining code quality and preventing regressions. Here's a comprehensive approach to testing JavaScript applications.

Types of Tests

Unit Tests

Test individual functions or components in isolation:

// utils.js
export const calculateTotal = (items) => {
  return items.reduce((sum, item) => sum + item.price, 0);
};

// utils.test.js
import { calculateTotal } from './utils';

describe('calculateTotal', () => {
  it('should calculate total price correctly', () => {
    const items = [
      { price: 10 },
      { price: 20 },
      { price: 30 }
    ];
    expect(calculateTotal(items)).toBe(60);
  });

  it('should handle empty array', () => {
    expect(calculateTotal([])).toBe(0);
  });
});

Integration Tests

Test how different parts work together:

// api.test.js
import { fetchUserData } from './api';
import { formatUserProfile } from './utils';

describe('User data flow', () => {
  it('should fetch and format user data', async () => {
    const userData = await fetchUserData(1);
    const profile = formatUserProfile(userData);
    
    expect(profile).toHaveProperty('displayName');
    expect(profile).toHaveProperty('email');
  });
});

Testing Tools

Jest

The most popular JavaScript testing framework:

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
  moduleNameMapping: {
    '^@/(.*)$': '<rootDir>/src/$1'
  }
};

Testing Library

For testing React components:

import { render, screen, fireEvent } from '@testing-library/react';
import LoginForm from './LoginForm';

test('should submit form with correct data', () => {
  render(<LoginForm onSubmit={mockSubmit} />);
  
  fireEvent.change(screen.getByLabelText('Email'), {
    target: { value: 'test@example.com' }
  });
  
  fireEvent.click(screen.getByRole('button', { name: 'Login' }));
  
  expect(mockSubmit).toHaveBeenCalledWith({
    email: 'test@example.com'
  });
});

Best Practices

  1. Write tests first (TDD approach)
  2. Test behavior, not implementation
  3. Use descriptive test names
  4. Mock external dependencies
  5. Aim for good coverage, but focus on critical paths

Async Testing

// Testing async functions
test('should handle async operations', async () => {
  const data = await fetchData();
  expect(data).toBeDefined();
});

// Testing with fake timers
test('should debounce function calls', () => {
  jest.useFakeTimers();
  const mockFn = jest.fn();
  const debouncedFn = debounce(mockFn, 1000);
  
  debouncedFn();
  debouncedFn();
  debouncedFn();
  
  jest.runAllTimers();
  expect(mockFn).toHaveBeenCalledTimes(1);
});

Related Topics

This builds on fundamentals and async-programming concepts.

For React-specific testing, see react/component-patterns.

Backend testing patterns are covered in backend/api-design.

#javascript #testing #jest #quality