시작하기에 앞서 이 포스트 내용은 Next.js 의 공식 문서를 참조하여 기록한 것임을 알립니다.
Jest 와 RTL(React Testing Library)는 유닛테스트를 위해 흔히 같이 사용된다.
다음과 같이 Next.js 프로젝트 환경에서 Jest를 사용할 수 있는 세 가지 환경 설정 방법이 있다.
하지만, 이번 페이지에서는 quickstart와 Babel 을 통해 사용하는 방법에 대해서만 자세히 알아볼 것이다.
#임시로 패키지를 설치하고 싶을 때
$npx create-next-app --example with-jest with-jest-app
# yarn 패키지 매니저 사용 시
$yarn create next-app --example with-jest with-jest-app
# npm 패키지 매니저 사용 시
$npm create next-app --example with-jest with-jest-app
Next.js 프로젝트 앱이 생성된 이후, Jest 를 사용하려면 아래의 패키지들을 개발 전용 설치 옵션으로 설치해줘야 한다.
(단, 해당하는 프로젝트의 Next.js version ≥ 12.x.x 일 경우, 기본으로 내장되어 있기 때문에 따로 설치할 필요가 없다.)
jest-environment-jsdom
@testing-library/react
@testing-library/jest-dom
# yarn 패키지 매니저 사용 시
$yarn add --dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom
# npm 패키지 매니저 사용 시
$npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom
프로젝트의 루트 디렉터리에 jest.config.js
파일을 만들고 다음을 추가한다.
(Jest 공식 문서에서 각 구성 옵션에 대한 설명을 자세히 확인할 수 있다.)
// jest.config.js
module.exports = {
collectCoverage: true,
// on node 14.x coverage provider v8 offers good speed and more or less good report
coverageProvider: 'v8',
collectCoverageFrom: [
'**/*.{js,jsx,ts,tsx}',
'!**/*.d.ts',
'!**/node_modules/**',
'!<rootDir>/out/**',
'!<rootDir>/.next/**',
'!<rootDir>/*.config.js',
'!<rootDir>/coverage/**',
],
moduleNameMapper: {
// Handle CSS imports (with CSS modules)
// https://jestjs.io/docs/webpack#mocking-css-modules
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
// Handle CSS imports (without CSS modules)
'^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js',
// Handle image imports
// https://jestjs.io/docs/webpack#handling-static-assets
'^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `<rootDir>/__mocks__/fileMock.js`,
// Handle module aliases
'^@/components/(.*)$': '<rootDir>/components/$1',
},
// Add more setup options before each test is run
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'],
testEnvironment: 'jsdom',
transform: {
// Use babel-jest to transpile tests with the next/babel preset
// https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }],
},
transformIgnorePatterns: [
'/node_modules/',
'^.+\\.module\\.(css|sass|scss)$',
],
}
stylesheet와 image는 테스트에는 사용되지 않지만 가져오면 오류가 발생할 수 있기 때문에 모의로 만들어야할 필요가 있다.
프로젝트의 루트 디렉터리에 __mocks__
폴더를 생성 후, 폴더 안에 아래의 두 파일들을 생성한다.
fileMock.js
styleMock.js
// __mocks__/fileMock.js
module.exports = {
src: '/img.jpg',
height: 24,
width: 24,
blurDataURL: 'data:image/png;base64,imagedata',
}
// __mocks__/styleMock.js
module.exports = {}
(static assets 처리에 대한 자세한 정보는 Jest 공식 문서에서 확인할 수 있다.)
toBeInTheDocument()
매처가 테스트 작성을 더 쉽게 할 수 있게 해주는 것처럼 @testing-library/jest-dom
에는 테스트를 더 쉽게 작성할 수 있는 편리한 커스텀 매처 세트가 포함되어 있다.
jest.config.js
파일에 다음과 같은 옵션을 추가한다면 모든 test 파일에서 custom matcher 을 import하여 사용할 수 있다.
// jest.config.js
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']
위에 옵션 코드를 추가한 후 다음과 같이 jest.setup.js
파일에서 import 한다.
// jest.setup.js
import '@testing-library/jest-dom/extend-expect'
각각 테스트를 실행하기 전에 더 많은 설정 옵션들을 추가해야할 경우 보통 jest.setup.js
파일에 추가해야 한다.
프로젝트에서 Module Path Aliases를 사용할 경우 jsconfig.json(tsconfig.json)에 설정되어 있는 파일 경로 옵션과 일치시켜 가져올 수 있도록 jest.config.js
을 구성해야 한다. (ex_ jest.config.js
내에 moduleNameMapper
옵션)
// tsconfig.json or jsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": ["components/*"]
}
}
}
// jest.config.js
moduleNameMapper: {
'^@/components/(.*)$': '<rootDir>/components/$1', // 위의 jsconfig(tsconfig)의 paths와 일치시킴
}
watch mode 기반의 jest 실행 스크립트를 package.json
에 추가한다.
// package.json
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"test": "jest --watch" // --watch: test 파일이 업데이트 될 경우, 자동으로 재실행됨.
}
$yarn test
$npm test
이제 프로젝트를 테스트할 환경은 모두 준비가 되었다.
이제 Next.js 프로젝트의 루트 디렉터리에 __tests__
폴더를 생성하고 해당 폴더 내부에 테스트 코드 파일들을 생성한다.
예를 들어 <Home/>
이라는 컴포넌트 구성 요소들 중 ‘제목’ 요소가 성공적으로 렌더링 되는지 테스트 하는 코드를 작성하면 아래와 같다.
// __tests__/index.test.jsx
import { render, screen } from '@testing-library/react';
import Home from '../pages/index';
import '@testing-library/jest-dom';
describe('Home', () => {
it('renders a heading', () => {
render(<Home/>); // 1. Home 페이지 컴포넌트 렌더링
// 2. 렌더링 된 화면(screen)dptj <name: 'welcome to next.js'> 라는 프로퍼티를 가지고 있는
// <h$ name: 'welcome to next.js'>...</h$> 태그 요소를 heading 변수로 정의
const heading = screen.getByRole('heading', {
name: /welcome to next\.js!/i,
});
// 3. heading 변수가 DOM 안에 존재하는지 테스트
expect(heading).toBeInTheDocument();
});
});
필요에 따라 스탭샷 테스트를 투가하여 구성요소에 대한 예기치 않은 변경 사항을 추적할 수 있다.
// /__tests__/snapshot.js
import { render } from '@testing-library/react';
import Home from '../pages/index';
it('render homepage unchanged', () => {
const { container } = render(<Home />);
expect(container).toMatchSnapshot();
})
*참고 : 테스트 파일은 page 디렉토리 내부에 포함되면 안된다. ( 테스트 파일은 page 디렉토리 내부의 모든 파일이 경로로 간주되기 때문)