테스트 라이브러리 도입 이유
- 기존에 프로젝트를 진행할 때, api 개발을 할 때에는 시간에 쫓겨 dev 환경에서만 value를 직접 입력하여 결과를 지켜보는 식으로 수행했었다.
- 이런 방식은 UI를 보면서 하니 직관적으로 보인다는 장점은 있지만... 그 부분을 개발한 사람만 알고 있는 과정이라 조금 불편할 수도있다는 생각이 들었다.
Vitest 설치 및 적용
- Next.js는 기본적으로 Webpack 번들러를 이용하기에, Vitest를 사용하려면 설치 후 일부 설정을 해주어야 한다.
npm install -D vitest @vitejs/plugin-react jsdom @testing-library/react @testing-library/dom
# 또는
yarn add -D vitest @vitejs/plugin-react jsdom @testing-library/react @testing-library/dom
# 또는
pnpm install -D vitest @vitejs/plugin-react jsdom @testing-library/react @testing-library/dom
# 또는
bun add -D vitest @vitejs/plugin-react jsdom @testing-library/react @testing-library/dom
- 해당 명령어를 설치하면, vitest관련 라이브러리가 설치 된다.
//vitest.config.ts
import type { NextConfig } from "next";
import path from 'path';
const nextConfig: NextConfig = {
webpack: (config) => {
config.resolve.alias = {
...config.resolve.alias,
'@': path.resolve(__dirname, './src'),
}
return config
},
};
export default nextConfig;
- 위와같이 설정을 적용하면, Vitest사용이 가능하다.
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage"
- 이후 package.json의 script에 테스트관련 명령어를 추가하면 끝
- 간단하게 설명을 하면,
- test : 전체 테스트 수행
- test:ui : UI로 보면서 테스트 결과 보기 가능(웹 페이지가 열림)
- test:coverage: 커버리지 테스트
간단하게 테스트 해보기
- 파일을 만들 땐, __test__ 폴더를 제작하거나 컴포넌트와 같은 위치에 test 파일을 만들 수 있는데, 나는 후자를 택했다.
- 진행중인 프로젝트에서 회원 가입을 테스트 하기 위해, 각각
- 이메일 중복체크
- 닉네임 중복체크
- 비밀번호 양식확인
- 비밀번호 - 비밀번호 확인 일치 여부
- 회원가입 요청
- 이 다섯가지로 크게 잡아 간단하게 테스트 해보기로 했다.
vi.mock('app폴더 위치', () => ({
getEmailCheck: vi.fn(),
getNicknameCheck: vi.fn(),
postSignup: vi.fn()
}))
vi.mock('next/navigation', () => ({
useRouter: () => ({
push: vi.fn()
})
}))
- 위와같이, api나 navigation 같은 메서드들을 모킹하여 사용할 수 있다.
- 테스트 시에는 전체적으로 describe 내의 it들을 순차적으로 테스트하며, 각각의 결과를 반환해준다.
-
describe('Signup Form', () => {
beforeEach(() => {
vi.clearAllMocks()
})
//1. 닉네임 중복체크
it('닉네임 중복 체크가 실패하면 회원가입 버튼이 비활성화되어야 함', async () => {
vi.mocked(getNicknameCheck).mockResolvedValue({
isAvailable: false,
reason: '이미 사용 중인 닉네임입니다.'
})
render(<Signup />)
const nicknameInput = screen.getByPlaceholderText('닉네임을 입력해주세요.')
fireEvent.change(nicknameInput, { target: { value: 'testuser' } })
const nicknameCheckButton = screen.getByText('닉네임 중복 확인')
fireEvent.click(nicknameCheckButton)
await waitFor(() => {
expect(screen.getByText('이미 사용 중인 닉네임입니다.')).toBeInTheDocument()
})
const submitButton = screen.getByText('회원가입')
expect(submitButton).toBeDisabled()
})
//2. 이메일 중복...
//3. 비밀번호 양식...
//4....
})
- 각각의 테스트해줄 내용에 대해 it으로 감싸서 만들어주면된다. 간단하게 설명하자면...
1. vi.mocked : 모킹한 api함수를 가져올 수 있다. 이후 mockResovled 또는 mockRejected 등으로 성공 / 실패로 직접 설정하여 값을 수정하면 되겠다.
2. render: 테스트할 컴포넌트
3. fireEvent : 사용자가 입력할 수 있는 값을 직접 넣어보는거나 클릭하거나...
4. waitFor: 해당 테스트가 끝날때 까지 기다릴 수 있도록 하는 구문으로, 비동기 처리를 지원한다.
5. expect : 궁극적으로 사용자가 원하는 결과값이다. 저게 성공적으로 수행되어야 테스트는 PASS를 반환한다.
- 이후 터미널에 npm test:ui를 수행하면, 다음과 같이 테스트를 수행해준다.
- Fail이 발생한다면 이유가 터미널에 표시가 되므로, 오류 문구를 읽고 해결할 수 있다.
써보고 나서...
- 사실 처음에 코드 작성할 때는 귀찮은 것 같은데 왜 쓰는지 모르겠다고 생각했으나, 작성하고 나니 이제 UI를 직접 켜서 굳이 불필요한 직접적인 테스트를 안해도 된다는 점에서 DX가 향상됐다고 생각한다.
- 이후에 같이 프로젝트하는 분들에게도, 주석만 잘 달아 놓는다면 이 코드를 수행하면서 코드에 대한 전반적인 내용을 알 수 있지 않을까 하는 기대감도 있었다!