JavaScript テスト入門
JavaScriptアプリケーションのテスト手法について、単体テストから統合テストまで基本的な考え方を解説します。
JavaScript テスト入門
品質の高いコードを書くために不可欠なテストの基本を学びましょう。
単体テストの基本
// math.js
export function add(a, b) {
return a + b
}
export function multiply(a, b) {
return a * b
}
export function divide(a, b) {
if (b === 0) {
throw new Error('Division by zero')
}
return a / b
}
// math.test.js
import { add, multiply, divide } from './math.js'
describe('Math functions', () => {
test('add should return sum of two numbers', () => {
expect(add(2, 3)).toBe(5)
expect(add(-1, 1)).toBe(0)
expect(add(0, 0)).toBe(0)
})
test('multiply should return product of two numbers', () => {
expect(multiply(3, 4)).toBe(12)
expect(multiply(-2, 3)).toBe(-6)
expect(multiply(0, 5)).toBe(0)
})
test('divide should return quotient of two numbers', () => {
expect(divide(10, 2)).toBe(5)
expect(divide(7, 2)).toBe(3.5)
})
test('divide should throw error when dividing by zero', () => {
expect(() => divide(5, 0)).toThrow('Division by zero')
})
})
非同期コードのテスト
// api.js
export async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`)
if (!response.ok) {
throw new Error('User not found')
}
return response.json()
}
// api.test.js
import { fetchUser } from './api.js'
// fetchをモック
global.fetch = jest.fn()
describe('fetchUser', () => {
beforeEach(() => {
fetch.mockClear()
})
test('should return user data when API call succeeds', async () => {
const mockUser = { id: 1, name: 'John Doe' }
fetch.mockResolvedValueOnce({
ok: true,
json: async () => mockUser
})
const user = await fetchUser(1)
expect(fetch).toHaveBeenCalledWith('/api/users/1')
expect(user).toEqual(mockUser)
})
test('should throw error when API call fails', async () => {
fetch.mockResolvedValueOnce({
ok: false
})
await expect(fetchUser(999)).rejects.toThrow('User not found')
})
})
Reactコンポーネントのテスト
// Button.jsx
export function Button({ onClick, disabled, children }) {
return (
<button onClick={onClick} disabled={disabled}>
{children}
</button>
)
}
// Button.test.jsx
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './Button'
describe('Button component', () => {
test('renders button with text', () => {
render(<Button>Click me</Button>)
expect(screen.getByRole('button')).toBeInTheDocument()
expect(screen.getByText('Click me')).toBeInTheDocument()
})
test('calls onClick when clicked', () => {
const handleClick = jest.fn()
render(<Button onClick={handleClick}>Click me</Button>)
fireEvent.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
test('is disabled when disabled prop is true', () => {
render(<Button disabled>Click me</Button>)
expect(screen.getByRole('button')).toBeDisabled()
})
})
テスト設定(Jest)
// jest.config.js
export default {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
moduleNameMapping: {
'\\.(css|less|scss)$': 'identity-obj-proxy'
},
collectCoverageFrom: [
'src/**/*.{js,jsx}',
'!src/index.js',
'!src/setupTests.js'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
}
// src/setupTests.js
import '@testing-library/jest-dom'
テストを書くことで、バグの早期発見とコードの品質向上を実現できます。