1. JavaScript Unit Test
- Unit Test: 통합테스트에 비해 빠르고 쉬움, 통합테스트 전 문제 발견 가능, 테스트 코드의 동작을 설명하는 명세가 됨
-> 🌟 선 코딩 후 몰아서 단위테스트 하지 말고, 단위테스트 작성 후 맞춰서 코딩하기 ; TDD(Test Driven Development)
2. Jest
- Testing Frameork, 페이스북의 오픈소스, 가장 핫한 테스트 도구
- 특징: Easy Setup, Instant Feedback, Snapshot testing
$ npm init -y $ npm i jest -D 이후 ./package.json의 script에 "test": "jest"
test("adds 1 + 2 to equal 3",() => {
expect(1+2).toBe(3);
});
describe("expect test", () => {
it("37 to equal 37",()=>{
expect(37).toBe(3);
});
it("{age: 39} to equal {age: 39}", () => {
expect({age: 39}).toBe({age: 39});
});
it("{age: 39} to equal {age: 39}",()=>{
expect({age: 39}).toEqual({age: 39});
});
it(".toHaveLength", () => {
expect("hello").toHaveLength(5);
});
it(".toHaveProperty", () => {
expect({name: "Mark"}).toHaveProperty("name");
expect({name: "Mark"}).toHaveProperty("name", "Mark");
});
it(".toBeDefined", () => {
expect({name: "Mark"}.name).toBeDefined();
expect({name: "Mark"}.age).toBeDefined();
});
it(".toBeFalsy", () => {
expect(false).toBeFalsy();
expect(0).toBeFalsy();
expect("").toBeFalsy();
expect(null).toBeFalsy();
expect(undefined).toBeFalsy();
expect(NAN).toBeFalsy();
});
it(".toBeGreaterThan", () => {
expect(10).toBeGreaterThan(9);
});
it(".toBeGreaterThanOrEqual", () => {
expect(10).toBeGreaterThanOrEqual(10);
});
it(".toBeInstanceOf", () => {
class foo{}
expect(new Foo()).toBeInstanceOf(Foo);
});
});
$ npx jest --watchAll
-> 항상 테스트가 켜져있음 (코드 고치면 자동 테스트 수행)
- 이외에
.toBeNull .toBeTruthy .toBeNaN 등
-> 의도에 따라 앞에 .not 붙여 .not.toBe .not.toBeFalsy 가능
+) 비동기적 결과물 test (async test)
- callback 사용, Promise 사용은 예전 방식
describe("use async test", () => {
it("async-await", async() => {
function p(){
return new Promise(resolve => {
setTimeout(() => {
resolve(37);
}, 1000);
});
}
const data = await p();
return expect(data).toBe(37);
});
});
describe("use async test", () => {
it("async-await, catch", async() => {
function p(){
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('error'));
}, 1000);
});
}
try {
await p();
} catch(error){
expect(error).toBeInstanceOf(Error);
}
});
});
describe ("use async test", ())
3. React Component Test
$ npx create-react-app react-component-test
-> create-react-app 으로 프로젝트 생성하면 이미 jest로 test 준비 끝낸 것 (따로 라이브러리 설치 필요 X)
- test 파일은 이름
.test.js로 만들거나 __tests__ 폴더 만들어 그 안에 넣기
4. testing-libraryreact 활용하기
- Button 컴포넌트 만들기 연습
1) button element는 HMTLButtonElement
2) 버튼 클릭하면 p 태그 안에 CLICKED 메세지 출력
3) 버튼 클릭 전 p 태그 안에 NORMAL 메세지 출력
4) 버튼 클릭하고 5초 뒤면 클릭 전(NORMAL) 상태로 출력
5) 버튼 클릭하면 5초 동안 비활성화
import {render} from "@testing-library/react";
import Button from "./Button"
const BUTTON_TEXT = {
NORMAL: '버튼이 눌리지 않았다.',
CLICKED: '버튼이 방금 눌렸다.'
}
describe('Button 컴포넌트 (@testing-library/react)', () => {
it('컴포넌트 생성', () =>{
const button = render(<Button />);
expect(button).not.toBe(null);
});
it('button은 HTMLButtonElement', () =>{
const {getByText} = render(<Button />);
const HMTLButtonElement = getByText('button');
expect(buttonElement).toBeInstanceOf(HTMLButtonElement);
});
it('버튼 클릭하면, p태그 안에 CLICKED 메세지 출력', () => {
const {getByText} = render(<Button />);
const buttonElement = getByText("button");
fireEvent.click(buttonElement);
const p = getByText(BUTTON_TEXT.CLICKED);
expect(p).not.toBeNull();
expect(p).toBeInstanceOf(HTMLParagraphElement);
});
it('버튼 클릭 전, p태그 안에 NORMAL 메세지 출력', () => {
const {getByText} = render(<Button />);
const buttonElement = getByText("button");
const p = getByText(BUTTON_TEXT.NORMAL);
expect(p).not.toBeNull();
expect(p).toBeInstanceOf(HTMLParagraphElement);
});
it('버튼 클릭 후 5초 뒤, p태그 안에 다시 NORMAL 메세지 출력', () => {
jest.useFakeTimers();
const {getByText} = render(<Button />);
const buttonElement = getByText("button");
fireEvent.click(buttonElement);
act(() => {
jest.advanceTimersByTime(5000);
});
const p = getByText(BUTTON_TEXT.NORMAL);
expect(p).not.toBeNull();
expect(p).toBeInstanceOf(HTMLParagraphElement);
});
it('버튼 클릭하면, 5초 동안 버튼 비활성화', () => {
jest.useFakeTimers();
const {getByText} = render(<Button />);
const buttonElement = getByText("button");
fireEvent.click(buttonElement);
expect(buttonElement).toBeDisabled();
act(() => {
jest.advanceTimersByTime(5000);
});
expect(buttonElement).not.toBeDisabled();
});
});
const BUTTON_TEXT = {
NORMAL: '버튼이 눌리지 않았다.',
CLICKED: '버튼이 방금 눌렸다.'
}
export default function Button(){
const[message, setMessage] = useState(BUTTON_TEXT.NORMAL);
const timer = useRef();
useEffect(() => {
return () => {
if(timer.current){
clearTimeout(timer.curent);
}
};
}, []);
return(
<div>
<button onClick={click} disabled={message === BUTTON_TEXT.CLICKED}>
button
</button>
<p>{message}</p>
</div>
);
function click(){
setMessage(BUTTON_TEXT.CLICKED);
timer.current = setTimeout(()=>{
setMessage(BUTTON_TEXT.NORMAL);
}, 5000);
}
}