@senspond
>
Jest는 메타(구 페이스북)가 유지보수하는 재스민(jasmine) 위에서 빌드되는 테스팅 프레임워크입니다. 이 글은 NextJs 프로젝트에 Jset 프레임워크를 적용하는 방법을 정리해봅니다.
Jest는 메타(구 페이스북)가 유지보수하는 재스민(jasmine) 위에서 빌드되는 테스팅 프레임워크입니다. 바벨, 타입스크립트, Node.js, 리액트, 앵귤러, Vue.js, Svelte를 사용하는 프로젝트에서 정상적으로 동작되고 초기설정이 간단한 편입니다.
자바 개발자라면 익숙한 Junit 테스트 프레임워크가 있는데요. 자바에 Junit이 있다면 자바스크립트에는 Jest가 있다고 볼 수 있겠습니다. 저는 이전에 Vue, React 환경에서 설정을 해봤던 적이 있었는데요. 현재 이 웹사이트의 프론트엔드는 NextJs 로 되어있는데, NextJs에서도 React에 적용하던 방식처럼 설정이 가능하였습니다. 그 내용을 정리해봅니다.
Jest 세팅하는 방법은 비슷하지만 환경에 따라서 약간의 차이가 있을 수 있어서 참고 용으로 기록합니다.
React18 / NextJs13 / TailwindCSS
TypeScript
npm i -D jest @types/jest
"scripts": {
"test": "jest"
},
그리고 [name].test.js 형식의 파일을 작성하면 됩니다.
아래는 예시를 들기 위해 샘플코드를 만들어 보았습니다.
const fs = require("fs");
test("hello test", async ()=>{
console.log("hello");
const data = await fs.readFileSync(__dirname + "/hello.test.ts", "utf-8")
console.log(data)
})
npm run test 로 모든 테스트케이스를 수행할 수도 있고 아래와 같이 인텔리제이 같은 개발툴에서 해당 테스트 케이스에서 run 버튼을 클릭해 개별적으로 수행해줄 수 있다.
테스트코드 파일의 위치를 어디에 둬야만 가능하다는 정해진 룰은 없지만 저는 테스트코드와 소스코드는 별개의 디렉토리에 두는 것을 선호하여 src 디렉토리에는 소스코드만 담고, __tests__
라는 분리되어 있는 디렉토리를 만들어서 넣어줬습니다.
React환경에서 ES Module Import 형태로 사용하기 위해서는
npm i -D @babel/core @babel/preset-env @babel/preset-typescript
babel.config.json 을 다음과 같은 설정해주게 되면 가능해집니다.
{
"presets": ["@babel/preset-env", "@babel/preset-typescript"]
}
오류가 사라지는 것을 확인 할 수 있습니다.
그런데 NextJs에서는 서버구동시 건드린 바벨설정으로 인해서 오류가 발생할 수가 있는데요. NextJs에서는 커스텀 babel 설정을 삭제하고 jest.config.js 파일을 만들고 transform 아래처럼 넣어주어도 됩니다.
module.exports = {
transform: {
"^.+\\.(js|jsx|ts|tsx)$": ["babel-jest", { presets: ["next/babel"] }],
},
};
jest 파일에서 외부모듈을 불러와 테스트를 할때
import {AppService} from '../../../middleware/services/apps/AppService'
와 같이 써야하는 것을
import {AppService} from '@/middleware/services/apps/AppService'
이런식으로 쓸 수 있게 만들어주는 설정이 필요한데요. 그 방법을 정리해봅니다.
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
],
}
TypeScript 를 사용중이라면 tsconfig.json에 이렇게 설정을 해주어야 합니다.
module.exports = {
moduleNameMapper: {
'@/(.*)$': '<rootDir>/src/$1',
},
transform: {
"^.+\\.(js|jsx|ts|tsx)$": ["babel-jest", { presets: ["next/babel"] }],
},
};
jest 설정파일을 만들어서 moduleNameMapper 에 경로를 지정해 정의하여 줍니다.
"scripts": {
"test": "jest --config jest.config.js"
},
test 커맨드로 jest 런타임 실행시 해당설정을 사용하도록 설정합니다.
npm i -D jest-environment-jsdom @testing-library/jest-dom @testing-library/react
리액트 컴포넌트 테스트를 하려면 @testing-library/jset-dom 과 @testing-library/react를 설치해줘야 합니다. @testing-library/react는 리액트 컴포넌트 testing을 위한 가상 돔(Virtual DOM)을 제공해주기 때문에 웹 브라우저 없이도 렌더링을 수 있고 elements와 상호작용 가능하며 테스트가 가능합니다.
구버전에서는 모듈명이 react-testing-library 였지만 @testing-library/react 로 변경되었습니다.
import '@testing-library/jest-dom';
jset.setup.js 파일은 Jest의 모든 테스트 코드에서 공통적으로 사용되는 설정이나 Mock 정의에 사용합니다.
module.exports = {
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
moduleNameMapper: {
'@/(.*)$': '<rootDir>/src/$1',
},
testEnvironment: "jest-environment-jsdom",
transform: {
"^.+\\.(js|jsx|ts|tsx)$": ["babel-jest", { presets: ["next/babel"] }],
},
};
위의 jest.config.js 보다 설정들이 추가 되었는데요. nextjs에서 react 컴포넌트를 jest를 통해 테스트를 하려면
testEnvironment: "jest-environment-jsdom",
transform: { "^.+\\.(js|jsx|ts|tsx)$": ["babel-jest", { presets: ["next/babel"] }],
이런 설정들이 필요합니다.
스냅샷 테스팅(snapshot testing)이란 어떤 기능의 예상 결과를 미리 정확히 포착해두고 실제 결과에 비교하는 테스트 기법입니다. 테스트 대상 기능의 구현이 변경되어 실제 결과과 스냅샷을 떠놓은 예상 결과와 달라질 경우 해당 테스트 케이스는 실패하게 됩니다. 이런 경우, 다시 새로운 스냅샷을 떠서 기존 스냅샷을 교체하는 방식으로 테스트 코드와 함께 스냅샷도 함께 유지보수를 합니다. Jest와 같은 테스팅 라이브러리를 사용하면 이러한 스냅샷 테스팅을 위한 일련의 과정을 좀 더 편하게 수행할 수 있습니다.
출처 : https://www.daleseo.com/jest-snapshot/
아래와 같이 React 컴포넌트를 작성하고 가상 DOM으로 랜더링하여 스냅샷 테스팅을 하는 샘플코드를 작성했습니다.
테스트 케이스를 수행하면,
import React from "react";
import {render} from "@testing-library/react";
const TestComponent = (props : {message : string}) : React.ReactElement=>{
return (
<div>
{props.message}
</div>
)
}
describe("snapshot test", ()=>{
it("should render", ()=>{
const component = render(<TestComponent message={"안내글입니다"} />)
expect(component.container).toMatchSnapshot();
});
})
__snapshot__
이라는 디렉토리에 랜더링된 결과가 다음과 같이 생성된것을 확인 할 수 있습니다.
그리고 snapshot 이 존재하는 상태에서 입력파라미터를 변경하고 다시 테스트를 수행하게 되면 TestCase는 실패되며 다음과 같이 출력됩니다.
MessageDialog.test.tsx 를 작성하고
import MessageDialog from "@/components/base/MessageDialog";
import {render} from "@testing-library/react";
describe("MessageDialog", ()=>{
it("should render", ()=>{
const component = render(<MessageDialog title = "안내" message="안내글입니다" isOpen={true}/>)
expect(component.container).toMatchSnapshot();
});
})
Tailwind CSS를 사용할 경우..
Could not parse CSS stylesheet 라는 오류를 만날 수 있습니다.
stackoverflow 에도 이 이슈에 대해서 언급된 내용이 있네요.
소개되어 있는 방법은 아래와 같습니다.
tailwindcss 를 컴파일해서 일반 css로 변환하고
npx tailwindcss -i ./src/index.css -o ./src/test/index.css
커스텀 Render 를 정의해서 그것을 사용하는 방식입니다.
import { render, RenderOptions } from '@testing-library/react';
import React, { FC, ReactElement } from 'react';
import fs from 'fs';
const wrapper: FC<{ children: React.ReactNode }> = ({ children }) => {
return <>{children}<>;
};
const customRender = (ui: ReactElement, options?: Omit<RenderOptions, 'wrapper'>) => {
const view = render(ui, { wrapper, ...options });
const style = document.createElement('style');
style.innerHTML = fs.readFileSync('src/test/index.css', 'utf8');
document.head.appendChild(style);
return view;
};
export * from '@testing-library/react';
export { customRender as render };
그런데 적용해보니 Next13 app route 방식을 사용하고 TailwindCSS 와 SCSS 를 같이 적용해 여러 파일로 나눠져있는
현 프로젝트에서는 이대로 적용할 수 없었습니다.
webpack 대신 Vite를 번들러로 사용하고 Vitest를 테스트 프레임워크로 사용하는 경우 좀더 간단하게 설정할 수 있다고 말하고 있는데, 추후에 신규 프로젝트에 그렇게 적용이 되는지 세팅을 한번 해보고 다시 정리해봐야 할것 같습니다.
안녕하세요. Red, Green, Blue 가 만나 새로운 세상을 만들어 나가겠다는 이상을 가진 개발자의 개인공간입니다.
현재글에서 작성자가 발행한 같은 카테고리내 이전, 다음 글들을 보여줍니다
@senspond
>