안녕하세요! 이번시간에는 react-testing-library를 사용하여 간단하게 몇가지 코드를 살펴보며 react에서는 테스트코드를 어떻게 짜는지를 알아보도록 하겠습니다!!
우선 테스트 코드를 잘 모르시는 분들을 위해 말씀드리면
테스트 코드란 쉽게말해서 우리가 개발한 코드들이 우리가 원하는 기대값으로 정상작동 하는지를 테스트 하는 코드를 짜는 것입니다!!
왜 테스트코드를 짜야할까?
개발하다보면 어쩔 수 없이 많은 legacy 코드들이 탄생하게 되고 반복적인 리팩토링 작업이 필요할때가 많습니다!
하지만 테스트 코드들이 짜여지 않은 상태에서 코드들을 수정하고 만지는 작업을 하게 되면 여러 컴포넌트들과 기능들이 의존관계가 있어 생각하지 못한 곳에서 side effect들이 발생하곤 하죠!
만약 우리가 테스트코드를 미리 잘 짜놓았고, 단위 및 통합테스트 코드를 잘 짜놓았다면 잘못된 결과값을 도출하는 코드를 짜놓았을때 개발자가 바로 현재 코드가 잘못되었다고 fail이라는 소식을 알려주게 됩니다!
때문에 개발자들은 이것을 인지하고 다른 방식으로 다시 개발을 진행하게 되겠죠. 이렇기때문에 테스트 코드는 지속적인 유지보수가 필요한 프로젝트에서는 매우 중요한 것입니다!! 개발자들에게 자신감을 높여줄 뿐더러 더욱더 깔끔한 코드도 만들 수 있게 해주죠!
그럼 바로 테스트 코드의 예시를 살펴보도록 하겠습니다
컴포넌트가 잘 렌더링이 되는지
import { render, screen } from "@testing-library/react"
import Person from "./Person"
test('내 이름이 들어간 컴포넌트가 잘 들어갔는지 테스트',()=>{
render(<Person name="신승훈"/>)
const element = screen.getByText(/제 이름은 신승훈입니다!/i);
expect(element).toBeInTheDocument();
})
import React from 'react';
const Person = ({name}) => {
return (
<div>
안녕하세요! 제 이름은 {name}입니다!
</div>
);
};
export default Person;
앵커태그의 href가 적상적으로 작동하는지 (우리가 원하는 페이지로 제대로 이동이 가는지 테스트)
import { render, screen } from "@testing-library/react"
import Link from "./Link"
test('앵커 element에 text가 적상 작동하고 링크 이동이 정상 작동하는지 테스트',()=>{
const items = [{
text:"후니블로그로 가보자!",
href:"https://www.hooneylog.com"
}]
render(<Link items={items}/>);
const elements = screen.getAllByRole("link");
expect(elements[0]).toHaveTextContent(items[0].text);
expect(elements[0]).toHaveAttribute("href",items[0].href);
})
import React from 'react';
const Link = ({items}) => {
return (
<>
{
items.map(({text,href})=><a href={href}>{text}</a>)
}
</>
);
};
export default Link;
버튼 클릭했을때 handleClick 함수가 정상 작동하는지 테스트
/* eslint-disable testing-library/no-render-in-setup */
import { fireEvent, render, screen } from "@testing-library/react"
import Button from "./Button"
beforeEach(()=>{
render(<Button/>);
})
describe('<Button/>',()=>{
test('초기 counte가 0으로 잘 렌더링이 되는지 테스트',()=>{
const element = screen.getByRole("countInfo");
expect(element).toHaveTextContent("Current Number is 0");
})
test('버튼을 클릭했을때 alert가 잘 동작하는지 테스트',async()=>{
const alertMock = jest.spyOn(window,"alert").mockImplementation();
const buttonElement = screen.getByRole("button",{
name:"카운트 올라가라!"
});
fireEvent.click(buttonElement);
expect(alertMock).toHaveBeenCalledTimes(1);
})
test('버튼을 클릭했을때 카운트가 잘 올라가는지 테스트',async()=>{
const buttonElement = screen.getByRole("button",{
name:"카운트 올라가라!"
});
fireEvent.click(buttonElement);
const divElement = screen.getByRole("countInfo");
expect(divElement).toHaveTextContent("Current Number is 1");
fireEvent.click(buttonElement);
expect(divElement).toHaveTextContent("Current Number is 2");
})
})
import React from 'react';
import { useState } from 'react';
const Button = () => {
const [count,setCount] = useState(0);
const handleClick = () => {
alert("등장!")
setCount(count + 1);
}
return (
<>
<div role="countInfo">Current Number is {count}</div>
<button onClick={handleClick}>카운트 올라가라!</button>
</>
);
};
export default Button;
Api 서버 Mocking 후 반환 state값이 잘 렌더링 되는지 테스트
import { render, screen } from '@testing-library/react'
import {rest} from 'msw'
import {setupServer} from 'msw/node'
import Api from './Api'
const server = setupServer(
rest.get('/user', (req, res, ctx) => {
return res(ctx.json({name: '신승훈'}))
}),
)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
test('api mocking 후 state 값이 잘 렌더링 되는지',async ()=>{
render(<Api/>);
const element = await screen.findByRole("contentInfo");
expect(element).toHaveTextContent("안녕하세요, 제 이름은 신승훈입니다.")
import React from 'react';
import { useState } from 'react';
import { useEffect } from 'react';
const Api = () => {
const [name,setName] = useState(null);
useEffect(()=>{
const dispatchFetch = async () => {
const response = await fetch("/user");
const result = await response.json();
setName(result.name)
}
dispatchFetch();
},[])
return (
<div>
{
name && <div role="contentInfo">
안녕하세요, 제 이름은 {name}입니다.
</div>
}
</div>
);
};
export default Api;
간단한 커스텀 훅 테스트
import { renderHook, act } from "@testing-library/react-hooks";
import useCounter from "./useCounter";
test("increment가 적상작동 하는지 테스ㅡㅌ", () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
import { useState, useCallback } from "react";
export default function useCounter() {
const [count, setCount] = useState(0);
const increment = useCallback(() => setCount((x) => x + 1), []);
return { count, increment };
}
이상으로 간단한 리액트 테스트 코드들을 살펴봤습니다!
감사합니다