단위 테스트 설정
Jest와 Testing Library를 Next.js에 설정하고 함수·컴포넌트의 출력과 의존성 mock을 빠르게 검증합니다.
품질 좋은 소프트웨어를 만들려면 테스트(testing)가 꼭 필요합니다. 특히 함수와 컴포넌트 같은 작은 단위를 검증하는 단위 테스트(unit testing)는 버그를 일찍 발견하고 리팩터링을 안전하게 하는 데 큰 도움을 줍니다. Next.js 프로젝트에서는 단위 테스트에 주로 Jest를 씁니다.
14장에서는 단위 테스트, 통합 테스트, E2E 테스트, 컴포넌트 테스트를 차례로 다루며 Next.js 애플리케이션을 검증하는 방법을 살펴봅니다. 이 글에서는 단위 테스트가 왜 중요한지, Jest를 Next.js 프로젝트에 설정하는 방법과 기본 사용법, 그리고 컴포넌트를 테스트하기 위해 @testing-library/react와 연동하는 방법을 알아봅니다.
Next.js 단위 테스트의 중요성
단위 테스트를 쓰면 개발 초기에 코드의 작은 부분에 숨어 있는 버그를 빠르게 찾아 고칠 수 있으며, 이는 통합 테스트나 배포 뒤에 버그를 발견하는 것보다 훨씬 비용이 적게 듭니다. 테스트할 수 있는 코드를 쓰려면 모듈로 잘 나뉘고 다시 쓰기 쉬운 코드로 설계해야 하므로, 자연스럽게 응집도가 높아지고 결합도가 낮아져 전반적인 코드 품질이 올라갑니다.
기존 코드를 개선하거나 바꿀 때 단위 테스트가 있으면, 변경이 기존 기능에 영향을 주지 않는지 빠르게 확인할 수 있어 리팩터링의 안전망이 됩니다. 또한 단위 테스트는 코드의 입력, 출력, 예외 조건을 실행 가능한 예시로 남겨 동작을 문서화하는 역할도 합니다.
Jest란?
Jest는 Meta(Facebook)가 만들고 지금은 OpenJS Foundation이 관리하는 JavaScript 테스트 프레임워크로, React 애플리케이션 테스트에서 특히 널리 쓰입니다. Jest는 설정 없이 시작하는 것(zero-configuration)을 지향하며, 테스트 러너, 어설션(assertion) 라이브러리, 모킹(mocking) 도구를 모두 내장하고 있어 간단한 JavaScript 테스트를 시작하기 쉽습니다. 다만 TypeScript, JSX, DOM 테스트에는 아래의 Next.js 변환과 환경 설정이 필요합니다.
Next.js 프로젝트에 Jest 설정하기
Next.js는 Jest를 위한 통합 설정을 제공하므로 설정 과정이 비교적 간단합니다. 그 중심은 next/jest라는 설정 도우미입니다. 이 도우미가 Next.js 컴파일러로 TypeScript와 JSX를 변환하고, CSS와 이미지 import를 테스트용 대역으로 바꾸며, .env 파일과 next.config의 설정을 읽어 테스트 환경에 반영합니다. 그래서 직접 할 일은 패키지 설치, 설정 파일과 setup 파일 작성, 실행 스크립트 등록의 네 단계이며, 아래에서 이 순서로 따라갑니다.
Jest 테스트 패키지 설치
먼저 Jest와, React 컴포넌트 테스트에 필요한 @testing-library/react 및 관련 패키지를 설치합니다. 모두 테스트할 때만 쓰는 도구이므로 --save-dev(yarn은 --dev)로 개발 의존성에 넣습니다. 브라우저 흉내를 내는 jest-environment-jsdom은 Jest 28부터 본체에 들어 있지 않으므로, 빠뜨리지 않고 함께 설치해야 합니다.
npm install --save-dev jest @types/jest @testing-library/react @testing-library/dom @testing-library/jest-dom jest-environment-jsdom
# 또는
yarn add --dev jest @types/jest @testing-library/react @testing-library/dom @testing-library/jest-dom jest-environment-jsdomjest는 Jest 테스트 프레임워크 본체이고, @types/jest는 TypeScript가 describe, test, expect, jest 전역 타입을 알아보게 합니다. @testing-library/react는 사용자가 컴포넌트와 상호작용하는 방식에 가깝게 테스트를 작성하도록 돕는 React 컴포넌트 테스트 도구이며, @testing-library/dom은 Testing Library의 DOM 쿼리이자 React 패키지가 직접 요구하는 peer 의존성입니다. @testing-library/jest-dom은 Jest에 DOM 관련 매처(matcher)를 더해 DOM 요소를 쉽게 검사하게 하고, jest-environment-jsdom은 Jest가 브라우저 환경을 흉내 내게 하는 환경으로 React 컴포넌트 테스트에 꼭 필요합니다. 지금 설치되는 @testing-library/jest-dom 7은 Node.js 22 이상을 요구하므로, Next.js 16의 최소 조건인 Node.js 20에서 실습하고 있다면 먼저 Node.js를 22 이상으로 올립니다.
Jest 설정 파일 생성
프로젝트 루트에 jest.config.js 파일을 만듭니다. 다음 코드는 CommonJS 설정이므로, package.json에 "type": "module"을 쓴다면 jest.config.cjs로 저장하고 이 글의 설정 파일 참조도 함께 바꿉니다. Next.js는 Jest 설정 가이드를 제공하며, 이를 바탕으로 설정할 수 있습니다.
jest.config.js의 예시는 다음과 같습니다.
const nextJest = require('next/jest');
// Next.js Jest 설정 유틸리티를 초기화합니다.
const createJestConfig = nextJest({
// Next.js 애플리케이션의 루트 디렉토리를 제공합니다.
dir: './',
});
// Jest에 추가적인 커스텀 설정을 추가할 수 있습니다.
const customJestConfig = {
// 테스트 환경을 jsdom으로 설정하여 DOM 환경을 시뮬레이션합니다.
testEnvironment: 'jsdom',
// 테스트 파일의 패턴을 정의합니다. (e.g., .test.js, .spec.js)
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|js?|tsx?|ts?)$',
// 모듈 해석 방식을 설정합니다.
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
// 테스트 전 전역적으로 실행될 setup 파일을 지정합니다.
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
// 특정 경로를 모듈로 인식하도록 합니다. Next.js의 path alias와 유사.
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
// 테스트 커버리지 보고서를 생성할지 여부를 설정합니다.
collectCoverage: false,
// 테스트 커버리지 보고서를 생성할 파일 패턴을 지정합니다.
collectCoverageFrom: [
'components/**/*.{js,jsx,ts,tsx}',
'lib/**/*.{js,jsx,ts,tsx}',
'!**/node_modules/**',
'!**/vendor/**',
],
};
// Next.js Jest 설정과 커스텀 설정을 병합하여 내보냅니다.
module.exports = createJestConfig(customJestConfig);이 글의 예제는 루트의 components와 lib에 둡니다. src 구조를 쓴다면 커버리지 glob과 예제 경로를 함께 src 기준으로 바꿉니다.
Jest Setup 파일 생성
jest.config.js의 setupFilesAfterEnv에 지정한 경로에 jest.setup.ts 파일을 만듭니다. 이 파일에는 각 테스트가 실행되기 전에 전역으로 실행할 코드를 넣으며, 주로 @testing-library/jest-dom을 import해 DOM 매처를 확장하는 데 씁니다.
jest.setup.ts의 예시는 다음과 같습니다.
import '@testing-library/jest-dom';
// 여기서는 @testing-library/jest-dom의 확장된 매처를 임포트합니다.
// 예: toBeInTheDocument(), toHaveClass() 등을 사용할 수 있게 됩니다.기본 Next.js tsconfig.json의 include에 **/*.ts가 들어 있으면 이 파일의 매처 타입도 함께 로드됩니다. include를 src로 좁혀 둔 프로젝트라면 jest.setup.ts를 명시적으로 추가합니다.
package.json에 테스트 스크립트 추가
package.json의 scripts에 Jest 실행 명령을 추가합니다. test는 모든 테스트를 한 번 실행하고 끝납니다. test:watch의 --watch는 터미널을 열어 둔 채 파일이 바뀔 때마다 바뀐 파일과 관련된 테스트만 다시 실행하는데, 이 판단에 Git 같은 저장소 정보를 쓰므로 저장소가 아닌 폴더에서는 모든 테스트를 다시 실행하는 --watchAll을 씁니다.
{
"name": "your-nextjs-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint .",
"test": "jest",
"test:watch": "jest --watch"
}
}이제 npm test나 yarn test로 Jest를 실행할 수 있습니다.
기본적인 단위 테스트 작성하기
함수와 컴포넌트의 단위 테스트 예시를 살펴보겠습니다. Jest 테스트 파일은 describe로 관련 테스트를 묶고, test 하나에 확인할 동작 하나를 적습니다. 각 test 안에서는 expect(실제값) 뒤에 toBe, toEqual 같은 매처를 붙여 기대값과 비교하며, 하나라도 맞지 않으면 그 test가 실패로 표시됩니다. 먼저 입력과 출력만 있는 순수 함수로 이 형식을 익히고, 이어서 화면을 렌더링하는 컴포넌트 테스트로 넘어갑니다.
순수 함수 테스트
가장 간단한 형태의 단위 테스트는 같은 입력에 언제나 같은 출력을 돌려주는 순수 함수를 검사하는 것입니다. 먼저 검사할 함수를 만듭니다.
export function sum(a: number, b: number): number {
return a + b;
}
export function capitalize(str: string): string {
if (!str) return '';
return str.charAt(0).toUpperCase() + str.slice(1);
}이 함수들의 입력과 기대 출력을 테스트로 적습니다. capitalize는 빈 문자열을 먼저 걸러 내므로, 테스트에도 ''를 넣어 그 분기를 확인합니다. 아래 파일은 __tests__ 폴더에 있어 앞에서 정한 testRegex에 맞으므로, npm test를 실행하면 Jest가 따로 등록하지 않아도 찾아서 실행합니다.
import { sum, capitalize } from '../lib/utils'; // 테스트할 함수 임포트
describe('Utils 함수 테스트', () => {
test('sum 함수는 두 숫자의 합을 반환해야 합니다.', () => {
expect(sum(1, 2)).toBe(3);
expect(sum(-1, 1)).toBe(0);
expect(sum(0, 0)).toBe(0);
});
test('capitalize 함수는 문자열의 첫 글자를 대문자로 만들어야 합니다.', () => {
expect(capitalize('hello')).toBe('Hello');
expect(capitalize('world')).toBe('World');
expect(capitalize('')).toBe('');
expect(capitalize('a')).toBe('A');
});
});일곱 개의 expect는 모두 함수의 실제 결과와 같으므로 두 테스트는 통과합니다. toBe는 Object.is로 비교하므로 숫자나 문자열 같은 원시값에 알맞습니다. 객체나 배열은 내용이 같아도 참조가 다르면 toBe에서 실패하므로, 뒤의 모킹 예제처럼 내용을 비교하는 toEqual을 씁니다.
React 컴포넌트 테스트
@testing-library/react는 컴포넌트의 내부 구현보다, 사용자 입장에서 컴포넌트가 어떻게 동작하는지를 테스트하도록 권장합니다. 다음 버튼 컴포넌트를 검사해 보겠습니다.
"use client"; // 클라이언트 컴포넌트
import React from 'react';
interface ButtonProps {
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
}
export default function Button({ children, onClick, disabled = false }: ButtonProps) {
return (
<button
onClick={onClick}
disabled={disabled}
style={{
padding: '10px 20px',
backgroundColor: disabled ? '#ccc' : '#007bff',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: disabled ? 'not-allowed' : 'pointer',
}}
>
{children}
</button>
);
}테스트는 버튼을 렌더링한 뒤, 사용자가 보는 글자로 버튼을 찾아 클릭하거나 상태를 확인합니다. render는 jsdom이 만든 가상 문서에 컴포넌트를 그리고, screen.getByText는 그 문서에서 글자가 일치하는 요소를 찾습니다. getBy로 시작하는 쿼리는 요소를 찾지 못하면 그 자리에서 오류를 던지므로, 요소가 없다는 사실만으로도 테스트가 실패합니다. 클릭은 fireEvent.click으로 일으키고, onClick이 불렸는지는 jest.fn()으로 만든 모의 함수의 호출 기록으로 확인합니다. toBeInTheDocument와 toBeDisabled는 앞의 jest.setup.ts에서 불러온 @testing-library/jest-dom이 더한 매처입니다.
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react'; // 필요한 유틸리티 임포트
import Button from '../components/Button'; // 테스트할 컴포넌트 임포트
describe('Button 컴포넌트', () => {
test('버튼 텍스트가 올바르게 렌더링되어야 합니다.', () => {
render(<Button>클릭하세요</Button>); // 컴포넌트 렌더링
// screen.getByText를 사용하여 화면에 "클릭하세요" 텍스트가 있는지 확인
expect(screen.getByText('클릭하세요')).toBeInTheDocument();
});
test('버튼 클릭 시 onClick 핸들러가 호출되어야 합니다.', () => {
const handleClick = jest.fn(); // Jest의 Mock 함수 생성
render(<Button onClick={handleClick}>Submit</Button>);
// 화면에서 "Submit" 텍스트를 가진 버튼을 찾고 클릭 이벤트를 발생시킵니다.
fireEvent.click(screen.getByText('Submit'));
// handleClick Mock 함수가 호출되었는지 확인합니다.
expect(handleClick).toHaveBeenCalledTimes(1); // 1번 호출되었는지
});
test('disabled prop이 true일 때 버튼이 비활성화되어야 합니다.', () => {
render(<Button disabled>비활성화 버튼</Button>);
// "비활성화 버튼" 텍스트를 가진 버튼이 disabled 상태인지 확인
expect(screen.getByText('비활성화 버튼')).toBeDisabled();
});
test('disabled prop이 false일 때 버튼이 활성화되어야 합니다.', () => {
render(<Button disabled={false}>활성화 버튼</Button>);
// "활성화 버튼" 텍스트를 가진 버튼이 enabled 상태인지 확인
expect(screen.getByText('활성화 버튼')).not.toBeDisabled();
});
});Jest는 async 서버 컴포넌트의 렌더링을 아직 지원하지 않습니다. 동기 서버 컴포넌트와 클라이언트 컴포넌트는 단위 테스트로 검사할 수 있지만, async 서버 컴포넌트는 E2E 테스트로 확인하는 것이 권장됩니다.
Mocking (모킹)
실제 서비스에서 API 호출이나 데이터베이스 접근 같은 외부 의존성이 있는 코드를 테스트할 때는 모킹(mocking) 기법을 씁니다. Jest는 모킹 기능을 제공해, 외부 의존성 없이 단위 테스트를 독립적으로 실행하게 돕습니다. 다음 함수의 URL은 로컬 서버를 가리키지만, 아래 테스트는 global.fetch를 대역으로 바꾸므로 실제 HTTP 요청을 보내지 않으며, 정상 응답과 오류 응답 객체를 테스트 안에서 준비합니다.
export async function fetchUserData(userId: string) {
const res = await fetch(`http://localhost:4000/users/${userId}`);
if (!res.ok) {
throw new Error('사용자 데이터를 가져오지 못했습니다.');
}
return res.json();
}테스트는 beforeEach에서 fetch를 대역으로 바꾸고, afterEach에서 원래대로 되돌립니다. jest.spyOn(global, 'fetch')는 전역 fetch를 호출 기록이 남는 함수로 바꾸고, mockResolvedValue는 호출될 때마다 준비한 가짜 응답으로 이행하는 Promise를 돌려주게 합니다. 오류 테스트는 mockResolvedValueOnce로 바로 다음 한 번의 호출에만 ok: false 응답을 돌려주게 덮어씁니다. 파일 맨 위의 @jest-environment node 주석은 이 파일만 jsdom 대신 Node.js 환경에서 실행하게 하며, 파일의 맨 처음에 있어야 적용됩니다.
/** @jest-environment node */
import { fetchUserData } from '../lib/api';
// fetch 함수를 Mocking
// Jest는 전역 fetch를 자동으로 Mocking 해주지는 않습니다.
// 파일 단위 node 환경에서 Node.js 20의 전역 fetch를 Mocking하는 예시입니다.
// 실제 Next.js 앱에서는 Jest 설정에서 'whatwg-fetch' 또는 'node-fetch'를 Mocking할 수 있습니다.
// 혹은 MSW (Mock Service Worker) 같은 라이브러리를 사용하는 것이 더 강력합니다.
describe('API 함수 테스트', () => {
// 테스트 시작 전에 fetch를 Mocking합니다.
beforeEach(() => {
jest.spyOn(global, 'fetch').mockResolvedValue(
{
ok: true,
json: () => Promise.resolve({ id: '123', name: 'Test User' }),
} as Response,
);
});
// 각 테스트 후 Mocking을 초기화합니다.
afterEach(() => {
jest.restoreAllMocks();
});
test('fetchUserData는 사용자 데이터를 성공적으로 가져와야 합니다.', async () => {
const user = await fetchUserData('123');
expect(user).toEqual({ id: '123', name: 'Test User' });
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(global.fetch).toHaveBeenCalledWith('http://localhost:4000/users/123');
});
test('fetchUserData는 오류 발생 시 예외를 던져야 합니다.', async () => {
// 오류 상황을 시뮬레이션하기 위해 fetch Mocking을 재정의
jest.mocked(global.fetch).mockResolvedValueOnce(
{
ok: false,
status: 404,
json: () => Promise.resolve({ message: 'Not Found' }),
} as Response,
);
await expect(fetchUserData('nonexistent')).rejects.toThrow('사용자 데이터를 가져오지 못했습니다.');
});
});NEXT · 14.1
실제 함수를 실행하고 fetch만 대역으로 바꿉니다
본문 테스트는 fetchUserData의 분기와 호출 인자를 확인합니다. global.fetch가 응답 객체를 제공하므로 localhost URL로 실제 HTTP 요청을 보내는 테스트는 아닙니다.
가로 실선은 함수 호출, 가로 점선은 준비된 응답 반환입니다. ok: true이면 json() 결과를 반환하고 ok: false이면 Error를 던집니다. beforeEach에서 spy를 만들고 afterEach에서 복원하며 실제 API·DB 연결은 별도 검사 범위입니다.
위 그림은 실제 함수는 그대로 실행하고 fetch만 대역으로 바꾸는 구조를 보여 줍니다. 실선은 함수 호출이고 점선은 응답 반환입니다. 실제 함수인 fetchUserData가 fetch(URL)을 호출하면, Jest 대역인 global.fetch가 준비해 둔 ok와 json()이 담긴 응답을 돌려줍니다. 그 응답이 ok: true이면 함수는 json()의 결과를 돌려주고, ok: false이면 Error를 던지며, 테스트는 그 값이나 예외, 그리고 fetch가 어떤 인자로 호출되었는지를 검사합니다. 그림의 설명처럼 beforeEach에서 spy를 만들고 afterEach에서 되돌리며, 실제 API나 DB 연결은 이 테스트가 검사하는 범위 밖입니다.
Jest 사용 시 고려사항 및 팁
테스트 파일은 보통 src와 같은 수준에 __tests__ 디렉터리를 만들어 두거나, 테스트할 파일과 같은 디렉터리에 .test.ts나 .spec.ts 접미사를 붙여 만듭니다. 모든 코드를 100% 테스트할 필요는 없으며, 업무 로직의 핵심, 복잡한 로직, 버그가 자주 생기는 부분을 먼저 테스트합니다.
Jest 같은 테스트 도구는 devDependencies로 관리합니다. 다만 이것만으로 테스트 소스가 배포물에서 저절로 빠지지는 않으므로, 제품 코드에서 테스트를 import하지 않고 빌드와 배포 파일의 범위를 나눕니다. 각 테스트는 특정 시나리오 하나만 검증하도록 간결하게 쓰고, 테스트 이름으로 목적과 기대 결과를 분명하게 나타냅니다.
테스트 주도 개발(TDD, Test-Driven Development)은 코드를 쓰기 전에 테스트를 먼저 작성하는 방식으로, 코드 설계와 품질을 높이는 데 도움이 될 수 있습니다. 스냅숏 테스트(snapshot testing)는 컴포넌트가 렌더링한 결과를 스냅숏으로 저장해 두고 이후 실행 때 현재 결과와 비교하는 방식으로 UI 변경을 추적하는 데 유용하지만, 남용은 피하는 것이 좋습니다.
실패를 다시 재현하려면 입력, 기대값, 대역 응답을 함께 남깁니다. next/jest는 변환을, testEnvironment는 실행 환경을, setupFilesAfterEnv는 공통 매처 준비를 맡으며, 실제 API와 브라우저 동작은 별도의 범위로 검증합니다.
정리
- 단위 테스트는 함수와 컴포넌트 같은 작은 단위를 검증해 버그를 일찍 찾고 리팩터링을 안전하게 합니다.
- Next.js에서는
next/jest로 변환 설정을 만들고,testEnvironment: 'jsdom'과setupFilesAfterEnv로 DOM 환경과 매처를 준비합니다. @testing-library/react는 사용자가 보는 글자와 역할로 요소를 찾아, 구현보다 동작을 검사하도록 돕습니다.- 외부 의존성은
jest.spyOn(global, 'fetch')같은 대역으로 바꾸고, 각 테스트 뒤에jest.restoreAllMocks()로 되돌립니다. - Jest는
async서버 컴포넌트를 렌더링하지 못하므로, 그런 컴포넌트는 E2E 테스트로 확인합니다.
연습 문제
1. 반환 경로를 직접 확인하기
대역 응답의 ok가 false이면 json()은 호출될까요? fetch 자체가 거부되거나, ok: true인 응답의 json()이 거부될 때도 함수가 같은 사용자 안내 오류로 바꾸는지 예측해 보세요.
해설 보기
ok: false이면 검사 지점에서 오류를 던지므로 json()까지 가지 않습니다. 반면 fetch나 json()의 거부를 바꾸는 catch는 없으므로, 그 거부는 그대로 호출한 쪽에 전달됩니다. 예제의 두 테스트는 성공 응답과 ok: false만 준비했으므로, 이 두 가지 거부까지 검증했다고 볼 수 없습니다.
추가로 테스트하려면 mockRejectedValueOnce로 fetch의 거부를 준비하고, 별도의 성공 응답의 json에는 거부되는 Promise를 넣습니다. 어떤 오류를 기대할지와 json이 호출되는지를 각각 정한 뒤 작성하세요. 실제 HTTP 연결은 여전히 대역 밖의 검사입니다.
2. 대역을 되돌리지 않으면
api.test.ts에서 afterEach(() => { jest.restoreAllMocks(); })를 지웠습니다. 같은 파일의 다른 테스트나 이후 테스트에는 어떤 영향이 생길 수 있을까요?
해설 보기
jest.spyOn으로 바꾼 global.fetch가 원래대로 돌아가지 않으므로, 앞 테스트에서 준비한 대역과 호출 기록이 다음 테스트에 남을 수 있습니다. 예를 들어 toHaveBeenCalledTimes(1)처럼 호출 횟수를 세는 검사가 앞 테스트의 호출까지 세어 실패할 수 있고, 실제 fetch를 기대한 테스트가 대역 응답을 받을 수도 있습니다. 테스트끼리 서로 영향을 주지 않도록 각 테스트 뒤에 대역을 되돌립니다.
3. 글자 대신 역할로 찾기
Button.test.tsx는 screen.getByText('Submit')으로 버튼을 찾습니다. screen.getByRole('button', { name: 'Submit' })으로 바꾸면 무엇이 달라질까요?
해설 보기
getByRole은 접근성 트리에서 button 역할과 이름이 "Submit"인 요소를 찾으므로, 사용자와 보조 기술이 버튼을 알아보는 방식에 더 가깝게 검사합니다. 같은 글자가 버튼이 아닌 다른 요소에도 있을 때 엉뚱한 요소를 잡는 일도 줄어듭니다. Testing Library도 가능하면 역할로 찾는 쿼리를 먼저 쓰도록 권장합니다.
Jest를 Next.js에 설정하는 공식 절차와 지원 범위는 Next.js의 Jest 설정 안내에서 확인할 수 있습니다.
다음 글 통합 테스트 작성에서는 여러 컴포넌트와 모듈이 함께 동작하는지 검사하는 통합 테스트를 작성합니다.