테스트코드 - 리액트 - vitest
vitest 기반 vanillaJS 테스트
vitest 설치
npm install -D vitest
sum.js
export function sum(a, b) {
return a + b
}
sum.test.js
import { expect, test } from 'vitest'
import { sum } from './sum'
describe('기본연산', () => {
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3)
})
})
용어
-
describe : test suites
-
it : test cases
-
expert.toBe : assertions
설정
package.json 실행 스크립트
{
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage"
}
}
이후에 npm run test 또는 yarn test 실행.
jsdom 설치
vitest 가 DOM 테스트를 가능하게 jsdom 설치(jsdom 이외에도 있음)
npm install --save-dev jsdom
vitest.config.js(ts)
vitest 환경 설정을 통해서 다양한 테스트 방법을 설정할 수 있음.
참고 : https://vitest.dev/config/
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom'
},
})
React 테스트
React Testing Library 설치
React DOM 요소를 선택하고 상호작용하는 것을 쉽게 만들어, 테스트가 실제 사용자의 행동을 더 잘 반영하게 하는 역할
npm install --save-dev @testing-library/jest-dom @testing-library/react @testing-library/user-event
setup 파일을 활용하면 유용
-
globals 설정을 true 해서, expect, describe, it 등의 테스트 함수를 수동으로 import 하지 않아도 됨.
-
setupFiles 을 설정해서 반복적인 수행을 미리 지정.
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
},
})
예) test/setup.js
//다양한 것을 import
import { afterEach } from 'vitest'
import { cleanup } from '@testing-library/react'
import '@testing-library/jest-dom/vitest'
// afterEach 와 같이 각 테스트 실행 후에 반복적으로 해야할 것을 설정.
afterEach(() => {
cleanup();
})
리액트 컴포넌트 테스트 코드 구현
-
학습팁 : react testing library 공홈의 예제를 참고하는것이 좋음
실습1. 기본 렌더링 테스트
-
App.test.jsx 테스트 파일 생성
-
렌더링 결과에
hello vite문자열이 있는지 확인하기 -
App을 렌더링 시키고,
-
assertion으로 렌더링 결과에 원하는 문자열이 포함되어 있는지 확인
-
npm run test 로 테스트 코드 실행
-
힌트 : testing-library/react 에 있는 다양한 함수를 활용
실습2. 이벤트 테스트
-
새로운 테스트 케이스 함수 작성
-
button 엘리먼트를 클릭하면 count 값 렌더링 결과가 ‘1’이 된다.
-
어떻게 이벤트를 발생 시킬 것인가
-
힌트 : testing-library/react 에 있는 다양한 함수를 활용
실습3. 커스텀 훅 테스트
-
테스트 코드 (/src/hooks/useCounter.test.js) 파일 생성
-
커스텀훅을 테스트 할 수 있는 방법을 찾아보자.
-
테스트 시나리오 예시)
-
count 초기값을 확인
-
increment 함수 호출
-
count값이 2인지 확인
-
실습4. 추가 기능 개발
- DataFromServer 컴포넌트를 App.jsx에서 호출해서 사용한다.
return (
<>
<h1>Hello Vite + React</h1>
<div className="card">
<button onClick={increment} data-testid="increment">
count is {count}
</button>
</div>
<DataFromServer todoId={count}></DataFromServer>
</>
);
실습5. fetch 테스트
-
DataFromServer.test.jsx 테스트 파일 생성
-
async/await, waitFor API 등을 활용해 테스트 진행
Mocking
- fetch 통신을 실제로 하지 않고 테스트 하는 방법은?
import { test, expect, vi } from 'vitest'
import { fetchData } from './api'
// 실제 fetchData 함수 (예시)
export async function fetchData() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1')
const data = await response.json()
return data
}
// Mocking fetch 함수
global.fetch = vi.fn(() => Promise.resolve({
json: () => Promise.resolve({ userId: 1, id: 1, title: 'mocked title', completed: false })
}))
test('fetchData 함수 호출 테스트 - Mocking fetch 사용', async () => {
const data = await fetchData()
expect(fetch).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/todos/1')
expect(data).toEqual({ userId: 1, id: 1, title: 'mocked title', completed: false })
})
테스트 커버리지(Test Coverage)
테스트가 코드의 얼마나 많은 부분을 실행하고 검증하는지를 측정하는 지표.
높은 커버리지는 코드의 많은 부분이 테스트되고 있음을 의미하며, 잠재적인 버그나 결함을 줄이는 데 도움이 됨. 하지만 커버리지가 높다고 해서 모든 오류가 잡힌다는 보장은 없으며, 테스트의 양뿐만 아니라 테스트의 질도 중요함.
커버리지 측정 확인(vitest 기반)
-
vitest.config.js 수정
-
예시)
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
exclude: [
'node_modules/',
'src/test/setup.ts',
]
}
},
});
- Vitest에서 커버리지를 확인하려면 package.json에 커버리지 스크립트를 추가.
{
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage"
}
}
- 이후
npm run coverage를 실행하면 커버리지 리포트를 확인할 수 있음.
커버리지 리포트 이해
생성된 커버리지 리포트에는 다음과 같은 정보들이 포함됨:
-
Statements: 테스트된 모든 코드 구문 비율
-
Branches: 조건문에서 분기가 테스트된 비율
-
Functions: 함수가 테스트된 비율
-
Lines: 실제 코드 라인이 테스트된 비율
Snapshot testing
Snapshot Testing은 컴포넌트의 UI가 예상대로 유지되는지를 확인하는 방법.
컴포넌트의 출력 결과를 스냅샷으로 저장하고, 이후 변경된 스냅샷과 비교하여 UI의 예기치 않은 변경을 방지함.
이를 통해 UI가 의도치 않게 변경되는 것을 쉽게 감지할 수 있음.
Snapshot Testing (Vitest기반)
Vitest에서 Snapshot Testing을 사용하려면 테스트 파일에서 expect().toMatchSnapshot()을 사용하면 됨.
예)
import { test, expect } from 'vitest'
import { render } from '@testing-library/react'
import MyComponent from './MyComponent'
test('MyComponent snapshot', () => {
const { container } = render(<MyComponent />)
expect(container).toMatchSnapshot()
})
이렇게 하면 MyComponent의 현재 출력 결과가 스냅샷으로 저장되고,
이후 테스트 시 스냅샷과 비교하여 변경 여부를 확인할 수 있음.
소프트웨어테스트
Software Test
software test에는 unit test를 포함해서 여러가지 테스트 개념이 있다.
아래 링크의 4가지 레벨이 일반적인 테스트 종류로 본다.
http://www.seguetech.com/the-four-levels-of-software-testing/
-
unit test : 소프트웨어의 최소단위, 보통 함수를 가리킴
-
Integration test : 단위 기능이 합쳐진 기능에 대한 테스트
-
System test : 위 내용보다 더 큰 개념, 전체 시스템에 대한 동작 테스트
-
Acceptance(인수) Test : 고객이 ok할 수 있는지 판단하기 위한 테스트
그리고,
-
UI test : FE에서 존재하는 개념으로, UI 기능 단위로 진행하는 테스트. 보통 Unit test와 system test 사이라고 볼 수 있음.
-
E2E test : End-to-end 테스트. 이 역시 UI 테스트와 같이 말하는 경우도 있고, 전체 시스템관점에서의 테스트로 보는 경우도 있음.
테스트를 하는 이유? 🤔
-
그냥 잘 돌아가는지 보기 위해서?
-
지속가능함을 유지하기 위해서 테스트는 중요한 장치임.
-
지속가능한 소프트웨어란?
-
즉시성 : 필요한 업데이트를 즉시에 할 수 있어야 함
-
건강함 : 지속적으로 건강한 모습
-
Unit Test
프로그래밍의 최소단위를 테스트 하는 것. 그 대상은 보통 함수.
사람이 직접 모든 함수를 호출하는 것은 불가능.
특정 함수를 호출하도록 테스트 코드를 구현하고, 이를 자동화해서 그 결과를 확인.
function print(arg) {
return 'result' + arg;
}
const expected = "result yes";
console.log(expected === print('yes')); // 성공(success)
console.log(expected === print('no')); // 실패(fail)
테스트코드 구현 잘 하기
1. 최소단위 함수부터 테스트하기
반환값이 명확히 존재하고, 다른 함수를 호출하지 않는 함수부터 테스트 한다.
즉, dependency 가 없는 함수.
2. 테스트 케이스 도출
-
어떤 함수를 테스트 할 것인가?
-
그 함수는 어떤 상황에서 어떻게 동작해야 하는가? (should be 표현..)
ex) 좌측 메뉴에서 아이템을 선택하면 선택한 아이템은 하이라이트 된다.
3. 테스트 가능한 함수로 리팩토링
- 현재 함수가 테스트 가능하지 못하면 테스트 가능한 함수로 변경.
4. given → when- > then 패턴
일관된 방식의 테스트 코드 구현을 위해서
given(테스트에 필요한 값 셋팅) → when(실행) → then(테스트)
방식으로 테스트 수행. 이 방법이 정답은 아니지만 가장 많이 쓰임.
describe('array test', function() {
it('equal dummy test', function() {
//given
var arr = [];
//when
arr.push(1,2,'3');
//then
assert.equal(arr.length, 3);
});
})
참고 : given(테스트에 필요한 값 셋팅) → when(실행) → then(테스트)
https://martinfowler.com/bliki/GivenWhenThen.html
테스트 프레임워크
많은 테스트 코드의 결과를 쉽게 확인하기 위해서 추가적인 장치가 필요.
Qunit, Mocha, Jest 와 같은 테스트 프레임워크를 사용하면 편리.
(앞으로는 Jest 를 기반으로 테스트 한다)
TDD(Test driven development)
개발 → 테스트코드 작성이라는 순서가 아니고 그 반대.
함수단위 테스트코드 구현 → fail → 함수구현 → pass → refactoring.
⇒ TDD는 단순한 테스트 코드 구현방식보다, 프로그래밍 설계 방법론에 가까움.
참고로, 비슷한 용어로 BDD(Behavior driven development)가 있음
BDD는 함수단위라기 보다는 하나의 인터랙션(시나리오)단위로 테스트 코드를 구현하는 방식.
TDD를 BDD형태로 구현할 수 있음. TDD가 함수단위라 그런 점의 차이점은 있으나, 두 개가 반대 개념은 아님.
![]()