본문 바로가기

dev/React

Next.js TypeScript 환경에서 Jest 초기 설정

create next app(CNA)에 typescript 옵션을 붙여 프로젝트를 생성한 후 Jest를 설정하는 방법입니다. CNA 을 사용할 경우 Jest 세팅이 되어 있지 않기 때문에 별도로 세팅을 해 주어야 합니다. 공식 문서 링크

 

Testing | Next.js

Learn how to set up Next.js with three commonly used testing tools — Cypress, Playwright, Jest, and React Testing Library.

nextjs.org

 

1. 필요 패키지 설치

테스트에 필요한 파일들을 설치합니다. 

$ yarn add -D @testing-library/dom @testing-library/jest-dom @testing-library/react @testing-library/user-event babel-jest jest jest-dom node-mocks-http ts-jest ts-loader

 

각 패키지의 기능은 다음과 같습니다.

- @testing-library 패키지들, babel-jest : @testing-libaray는 리액트 컴포넌트를 가상 브라우저에서 테스트하기 위한 기능들을 제공합니다. babel-jest는 테스트 코드를 변환하고 컴파일합니다.

- ts-jest, ts-loader : Jest에서 타입스크립트 기반의 코드를 테스트할 수 있게 해 줍니다.

- node-mocks-http : Next API routes를 테스트할 때 필요한 request, response 객체의 mock을 생성해 줍니다.

 

2. 설정 파일 구성

1) tsconfig 파일에 설정을 추가해 줍니다.

CNA에 타입스크립트 플래그를 사용했다면 tsconfig 파일이 이미 생성되어 있습니다(없을 경우 yarn dev를 한 번 실행하면 생성된다고 합니다). 따로 설정을 만질 것은 없습니다. 그냥 코드 작성 시 편의를 위해 Path alias만 추가해 줍니다. 아래와 같은 설정을 추가하면 relative path를 일일히 적을 필요 없이 @component 식으로 import 할 수 있어 편리합니다.

// tsconfig.json

{
    "compilerOptions": {
        {...}
        "baseUrl": ".",
        "paths": {
            "@components/*": ["components/*"],
            "@styles/*": ["styles/*"],
            "@pages/*": ["pages/*"],
            "@hooks/*": ["hooks/*"]
        }
    {...}
}

2) babelrc 파일에 프리셋을 설정해 줍니다.

// .babelrc.json


{
  "presets": ["next/babel"]
}

3) jest.config.json 파일을 설정해 줍니다.

root path에 jest.config.json 파일을 생성 후 설정들을 넣어줍니다. 아래 설정은 공식 문서에서 추천하는 설정입니다.

// jest.config.json

module.exports = {
  collectCoverageFrom: [
    "**/*.{js,jsx,ts,tsx}",
    "!**/*.d.ts",
    "!**/node_modules/**"
  ],
  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>/styles/__mocks__/styleMock.js",

    // Handle image imports
    // https://jestjs.io/docs/webpack#handling-static-assets
    "^.+\\.(jpg|jpeg|png|gif|webp|avif|svg)$": `<rootDir>/__mocks__/fileMock.js`,

    // Handle module aliases
    "^@pages/(.*)$": "<rootDir>/pages/$1",
    "^@components/(.*)$": "<rootDir>/components/$1"
  },
  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)$"]
};

- setupFilesAfterEnv 

테스트 실행 전에 셋업해야 할 내용들을 담은 파일입니다. 예시에서는 jest.setup.js 이라는 파일을 사용했습니다. 이 파일은 자동 생성되지 않기 때문에 반드시 root에 jest.setup.js 파일을 생성해 주셔야 합니다. 공식 문서에서는 간단하게 다음 코드만 사용하고 있습니다.

// jest.setup.js

import '@testing-library/jest-dom/extend-expect'

@testing-library/jest-dom 에 있는 custom matcher를 사용하기 위한 구문입니다.

 

- Path alias 설정(moduleNameMapper) 

절대 경로를 쓰지 않고 alias를 사용하기 위한 설정입니다. 타입스크립트의 경우 먼저 tsconfig 에 baseUrl과 paths를 설정해 주어야 합니다. 그 후, moduleNameMapper에 설정을 추가해 주면 됩니다. 저는 @component/* 같은 형태로 path alias를 사용하고 싶어서 아래와 같이 설정을 추가했습니다.

  moduleNameMapper: {
	...
    
    // Handle module aliases
    "^@pages/(.*)$": "<rootDir>/pages/$1",
    "^@components/(.*)$": "<rootDir>/components/$1"
  },

위와 같이 설정해 두면 테스트 파일에서 import 할 때 매번 절대 경로를 적을 필요 없이 @components/... 를 사용해 components 폴더의 파일들을 가져올 수 있습니다.

 

3. 컴포넌트 테스트

컴포넌트 테스트를 위해 먼저 테스트 폴더들을 생성해 줍니다. root에 __test__ 폴더를 만들고 하위 폴더들을 생성해 줍니다. 마지막으로 page의 index를 테스트하기 위한 index.test.jsx를 생성합니다.

그 후, 테스트 코드를 작성합니다.

// __test__/page/index.test.tsx

import React from "react";
import { render, screen } from "@testing-library/react";
import Home from "@pages/index"; // 위에서 path alias를 미리 설정해 두어서 편리하게 사용!

describe("Home", () => {
  it("renders a heading", () => {
    render(<Home />);

    const heading = screen.getByRole("heading", {
      name: /welcome to next\.js!/i
    });

    expect(heading).toBeInTheDocument();
  });
});

 

# document is not defined 에러 처리

  ● App › renders without crashing

    The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/configuration#testenvironment-string.
    Consider using the "jsdom" test environment.

    ReferenceError: document is not defined

테스트 코드 실행 시, 위와 같은 에러가 발생할 수 있습니다. 이는 Jest의 기본 테스트 환경이 node로 설정되어 있기 때문입니다. node에는 DOM 객체가 없기 때문에 react test liblary가 제대로 작동할 수 없습니다. 컴포넌트 테스트를 위해서는 테스트 환경을 jsdom으로 변경해 주어야 합니다. 해결책은 크게 두 가지가 있습니다.

1. jest.config.js에 설정을 추가하는 방법

jest.config.js에 "testEnvironment": "jsdom" 을  추가합니다. 설정을 추가하면 전역적으로 테스트 환경이 jsdom이 됩니다.

2. 각 테스트 파일에 @jest-environment jsdom 주석 추가

/**
 * @jest-environment jsdom
 */

각 테스트 파일 상단에 위와 같은 주석을 추가합니다. 이 방법은 해당 파일만 jsdom 환경에서 실행되도록 합니다.

4. API route 테스트

API route 테스트를 위해서는 http request 와 response를 mock 해야 합니다. node-mock-http 모듈을 사용해 간단하게 이 기능을 mock 할 수 있습니다.

 

테스트를 위해 아래와 같이 코드를 작성하고 실행합니다. CNA 사용 시 기본적으로 page/api 에 hello.ts 라는 api가 작성되어 있습니다. 이 api를 node-mock-http 에서 제공하는 createMocks 함수로 간단하게 mocking 하여 테스트하는 코드입니다.

import { createMocks } from 'node-mocks-http';
import handler from '@pages/api/hello';

describe('/api/hello', () => {
    test('returns a message with the specified name', async () => {
        const { req, res } = createMocks({
            method: 'GET',
        });

        await handler(req, res);

        expect(res._getStatusCode()).toBe(200);
        expect(JSON.parse(res._getData())).toEqual(
            expect.objectContaining({
                name: 'John Doe',
            })
        );
    });
});

참조

Setting up a Next.js Application with TypeScript, JIT Tailwind CSS and Jest/react-testing-library

 

Setting up Next.js with TypeScript, JIT Tailwind CSS and Jest/RTL

How I set up a modern stack with Next.js, TypeScript and Tailwind CSS with JIT compiler, fully ready for TDD with Jest and React Testing Library.

blog.antoniolofiego.com