React Test ์ฝ๋ ์์ฑ
2022. 06. 06
1. ๊ธฐ์ ์คํ
- react 18
- react-router-dom v6
- react-query
- testing-library
๊ธฐ์ ์คํ์ ๊ฒฝ์ฐ, React 18๊ณผ React-router-dom v6 ๊ณผ ๊ฐ์ด ์ต์ ๋ฒ์ ผ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์๊ณ , API ํธ์ถ์ ์ํ Axios์ ์๋ฒ ์ํ๊ด๋ฆฌ๋ฅผ ์ํ React-query ๋ฅผ ์ด์ฉํ์๊ณ , React ๊ณต์ ํํ์ด์ง์๋ ๊ฒ์๋์ด์๋ฏ Facebook์์๋ย React Testing Library ์ฌ์ฉ์ ๊ถ์ฅํ๋ ๋งํผ ํ
์คํธ ์ฝ๋ ์์ฑ์ Testing-library ๋ฅผ ์ด์ฉํ์ฌ ์งํํ์์ต๋๋ค.
2. React Testing Library
React Testing Library(RTL)์ ๊ธฐ์กด์ Enzyme์ ๊ฐ์ ๋๊ตฌ์ ๊ตฌํ ๊ธฐ๋ฐ์ ํ
์คํธ ๋ฐฉ๋ฒ๋ก ์ ๋จ์ ์ ๋ณด์ํ๊ธฐ ์ํด ๋ฑ์ฅํ์๊ณ , ํ์ ์ฃผ๋ ํ
์คํธ ๋ฐฉ๋ฒ๋ก ์ผ๋ก ํ
์คํธ๋ฅผ ์งํํฉ๋๋ค.
๋จผ์ , ๊ตฌํ ๊ธฐ๋ฐ์ ํ
์คํธ ๋ฐฉ๋ฒ๋ก ์ด๋, ์ฃผ๋ก ์ ํ๋ฆฌ์ผ์ด์
์ด ์ด๋ป๊ฒ ๋์ํ๋์ง์ ๋ํด์ ์ด์ ์ ๋์ด ํ
์คํธ ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
<div class="title">Hello</div>์๋ฅผ ๋ค์ด, ์์ ๊ฐ์ ์ฝ๋๊ฐ ์์ ๊ฒฝ์ฐ, ์ปดํฌ๋ํธ์ ์ด๋ค prop์ด ๋์ด๊ฐ๊ณ , ํ์ฌ state์ด ์ด๋ป๊ฒ ๋๋์ง, div ํ๊ทธ๊ฐ ์ฐ์๊ณ title์ด๋ผ๋ ํด๋์ค๊ฐ ์ฌ์ฉ๋์๋์ง ๋ฑ์ ํ
์คํธ ํฉ๋๋ค.
์ด์ ๋ฐํด, ํ์ ์ฃผ๋ ํ
์คํธ ๋ฐฉ๋ฒ๋ก ์ ๊ฒฝ์ฐ, ์ธ๋ถ์ ์ธ ๊ตฌํ์ฌํญ๋ณด๋ค๋ ์ค์ ์ฌ์ฉ์ ๊ฒฝํ๊ณผ ์ ์ฌํ ๋ฐฉ์์ผ๋ก ํ
์คํธ ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค. ์ฆ, ์ฌ์ฉ์ ์
์ฅ์์๋ div ํ๊ทธ๊ฐ ์ฐ์๋ค title์ด๋ผ๋ ํด๋์ค๊ฐ ์ฌ์ฉ๋์๋์ง ๋ณด๋ค๋ ํ๋ฉด์ ์ฌ๋ฐ๋ฅธ ์ฝํ
์ธ ๋ด์ฉ์ด ํ๊ธฐ๋๋๊ฐ ๋ผ๋ ๋ถ๋ถ์ด ๋ ์ค์ํ๊ธฐ ๋๋ฌธ์, RTL์ ๊ฒฝ์ฐ, Hello๋ผ๋ ํ
์คํธ๊ฐ ํ๋ฉด์ ์๋์ง๋ฅผ ํ
์คํธ ํฉ๋๋ค. ๋ฐ๋ผ์ ์ด์ ๊ฐ์ด ๊ตฌํ๋ณด๋ค ๊ธฐ๋ฅ์ ์ด์ ์ ๋ง์ถ ํ
์คํธ ๋ฐฉ์์ ์ ๋ขฐ๋๋ฅผ ๋์๊ณผ ๋์์ ์ฝ๋ ๋ฆฌํฉํ ๋ง ์ ํ
์คํธ ์ฝ๋ ์์ ๋น๋๋ฅผ ์ค์ผ ์ ์์ต๋๋ค.
3. ์ปดํฌ๋ํธ ๋ ๋๋ง ํ ์คํธ
๋จผ์ , ๊ฐ๋จํ ์์๋ฅผ ํตํด์ RTL์ ํ์ฉํด์ ์ด๋ป๊ฒ ํ ์คํธํ๋์ง ์์๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
const Home = () => {
return (
<section>
<h1>Hello Word</h1>
<p>์๋
ํ์ธ์. ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ MAENG์
๋๋ค.</p>
</section>
)
}์์ ๊ฐ์ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ง๊ณ ํ
์คํธ๋ฅผ ์งํํ ๊ฒฝ์ฐ, ์ฌ์ฉ์ ์
์ฅ์์ ์๊ฐํด๋ณด๋ฉด, ๋จผ์ Hello world ๋ผ๋ ๋ฌธ๊ตฌ์ ์๋
ํ์ธ์. ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ MAENG์
๋๋ค. ๋ผ๋ ๋ฌธ๊ตฌ๋ฅผ ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋์์๋ ํ๋ฉด์ ๋
ธ์ถ๋์ด์ผํ ๊ฒ์
๋๋ค.
RTL์ ๊ฐ์ง๊ณ ํด๋น ๋ด์ฉ๋ค์ด ์ ๋๋ก ๋ ธ์ถ๋๋์ง ํ ์คํธํ๊ธฐ ์ํด์๋ ๋ค์๊ณผ ๊ฐ์ด ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
describe('Home', () => {
it('renders Home', () => {
const { getByText } = render(<Home />)
expect(getByText('Hello world')).toBeInTheDocument()
expect(getByText('์๋
ํ์ธ์. ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ MAENG์
๋๋ค.')).toBeInTheDocument()
})
})4. ์ ์ ์ธํฐ๋ ์ ํ ์คํธ
์ด์ ๋ฒํผ์ด๋ ์
๋ ฅํ๋ ๋ฑ ์ ์ ์ธํฐ๋ ์
์ด ์ด๋ฃจ์ด์ง๋ ๋ถ๋ถ์ ์ด๋ป๊ฒ ํ
์คํธํ๋์ง ์ดํด๋ณผ ์ฐจ๋ก์
๋๋ค.
const Button = ({ onClick }) => {
return <button onClick={onClick}>๋ฒํผ</button>
}์์ ๊ฐ์ ๋ฒํผ ์ปดํฌ๋ํธ๊ฐ ์์ ๊ฒฝ์ฐ, ์ฌ์ฉ์ ์
์ฅ์์ ์๊ฐํด๋ณผ๋ "๋ฒํผ" ์ด๋ผ๋ ํ
์คํธ๋ฅผ ๊ฐ์ง ๋ฒํผ์ ํด๋ฆญํ ๊ฒฝ์ฐ, onClick ํจ์๊ฐ 1๋ฒ ์คํ๋ ๊ฒ์ ๊ธฐ๋ํ ์ ์์ต๋๋ค.
์์ ๋ด์ฉ์ ํ ์คํธ ์ฝ๋๋ก ์์ฑํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
describe('Button', () => {
it('renders Button', () => {
const onClick = jest.fn() // ๊ฐ์ง ํจ์
const { getByText } = render(<Button onClick={onClick} />)
expect(getByText('๋ฒํผ')).toBeInTheDocument()
fireEvent.click(button)
expect(onClick).toBeCalledTimes(1)
})
})์ ์ฝ๋๋ฅผ ๋ณด๋ฉด jest.fn() ์ ์ฌ์ฉํ์ฌ onClick ํจ์๋ฅผ ๋ชจํนํ๋๋ฐ ๋ชจํน์ ๋ํด์๋ ํ๋จ์ ๋ฐ๋ก ์ ๋ฆฌํ์ฌ ์ค๋ช
ํ๋๋ก ํ๊ฒ ์ต๋๋ค.
์ด๋ฒ์๋ ์ ๋ ฅ ํ๋์ ๊ฒฝ์ฐ ์ด๋ป๊ฒ ํ ์คํธ๋ฅผ ์งํํ๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
const Form = () => {
const [text, setText] = useState('')
return (
<form>
<input
type="text"
placeholder="ํ
์คํธ๋ฅผ ์
๋ ฅํด์ฃผ์ธ์."
value={text}
onChange={({ target: { value } }) => setText(value)}
/>
<button disabled={!text}>์ ์ถ</button>
</form>
)
}์์ ๊ฐ์ ๋ฒํผ ์ปดํฌ๋ํธ๊ฐ ์์ ๊ฒฝ์ฐ, ๋ง์ฐฌ๊ฐ์ง๋ก ์ฌ์ฉ์ ์
์ฅ์์ ์๊ฐํด๋ณด๋ฉด "ํ
์คํธ๋ฅผ ์
๋ ฅํด์ฃผ์ธ์." ๋ผ๋ placeholder๊ฐ ์๋ ์
๋ ฅํ๋์ ํ
์คํธ๋ฅผ ์
๋ ฅํ ๊ฒฝ์ฐ, ํด๋น ์
๋ ฅํ๋์ ๊ฐ์ ์ฌ์ฉ์๊ฐ ์
๋ ฅํ ๊ฐ์ผ๋ก ๋ณํ ๊ฒ์ด๊ณ , "์ ์ถ" ์ด๋ผ๋ ํ
์คํธ๋ฅผ ์ง๋ ๋ฒํผ์ด ํ์ฑํ ๋ ๊ฒ์
๋๋ค.
์์ ๋ด์ฉ์ ํ ์คํธ ์ฝ๋๋ก ์์ฑํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
describe('Form', () => {
it('renders Form', () => {
const { getByText, getByPlaceholderText } = render(<Form />)
const input = getByPlaceholderText('ํ
์คํธ๋ฅผ ์
๋ ฅํด์ฃผ์ธ์.')
const button = getByText('์ ์ถ')
expect(button).toBeDisabled()
fireEvent.change(input, { target: { value: 'Maeng Input Test' } })
expect(input.value).toBe('Maeng Input Test')
expect(button).toBeEnabled()
})
})5. Custom Hook ํ ์คํธ
์์ฆ React์์๋ ํจ์ํ ์ปดํฌ๋ํธ์ ์ฌ๋ฌ๊ฐ์ง ํ ์ ์ด์ฉํด์ ๊ฐ๋จํ๋ฉด์๋ ์ฌ์์ฐ์ฑ์ ๋์ธ ๋ฐฉ์์ผ๋ก ํ๋ก๊ทธ๋๋ฐ์ ํ๊ธฐ์ ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ๋ฐ๋ณต์ ์ผ๋ก ์ฌ์ฉ๋๊ฑฐ๋ ๋ณต์กํ ๊ณ์ฐ์ด ํฌํจ๋ ํ ๋ค์ ๋ณ๋์ ์ปค์คํ ํ ์ผ๋ก ๋ง๋ค์ด ๊ด๋ฆฌํ๋๊ฒ ์ฉ์ดํฉ๋๋ค. ๋ฐ๋ผ์ React๋ฅผ ํ ์คํธํ ๋ ๋จ์ ์ปดํฌ๋ํธ ๋ ๋๋ง๋ฟ ์๋๋ผ ์ด๋ฌํ ํ ๋ค์ ๋ํ ํ ์คํธ๋ ์ด๋ฃจ์ด์ ธ์ผํฉ๋๋ค.
๋ฆฌ์กํธ ์ปค์คํ ํ ์ ํ ์คํธํ๊ธฐ ์ํด์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด Hook ํ ์คํธ
- Custom Hook์ ์ฌ์ฉํ๋ ํ ์คํธ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด ํ ์คํธ
@testing-library/react-hooks์ยrenderHook์ ์ฌ์ฉ
์ ๋ ์ด์ค์์ @testing-library/react-hooks ๋ฅผ ์ด์ฉํด์ ์ปค์คํ
ํ
์ ํ
์คํธํ๋ ๋ฐฉ๋ฒ์ ๋ํด์ ์ค๋ช
ํ๊ฒ ์ต๋๋ค.
@testing-library/react-hooks ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ๋ฉด ๋ณ๋์ React Component์ ๋์ ์์ด๋ React Hook์ ์ง์ ํ
์คํธ๋ฅผ ํ ์๊ฐ ์์ต๋๋ค.
์ง์ ์ฝ๋๋ฅผ ์์ฑํ์ฌ ์ค๋ช ๋๋ฆฌ๋๋ก ํ๊ฒ ์ต๋๋ค.
const useInput = (initialValue = null) => {
const [value, setValue] = useState(initialValue)
const handler = useCallback((e) => {
setValue(e.target.value)
}, [])
return [value, handler]
}์์ ย useInput ์ด๋ผ๋ ์ปค์คํ
ํ
์ย ์
๋ ฅ ๊ฐ์ ๋ด์ย value ์ํ๊ฐ๊ณผ ๊ทธ ์ํ๊ฐ์ ์์ ํ ์ ์๋ย handle ๋ฅผ ๋ฐฐ์ด์ ๋ด์ ๋ฐํํฉ๋๋ค. ์ด๊ธฐ ์ํ๊ฐ์ย initialState ์ธ์๋ฅผ ํตํด ๋ฐ์ ์ ์์ผ๋ฉฐ, ์ธ์๋ฅผ ๋๊ธฐ์ง ์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ์ผ๋กย null์ ์ฌ์ฉํฉ๋๋ค.
Custom Hook ํ ์คํธ์ ๊ฒฝ์ฐ, ์ปดํฌ๋ํธ์ฒ๋ผ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ง๋ ๋ถ๋ถ์ด ์๋๋ค ๋ณด๋ ์ปดํฌ๋ํธ ํ ์คํธ์๋ ์กฐ๊ธ ๋ค๋ฅด๊ฒ ์ฌ์ฉ์ ์ ์ฅ์ด ์๋ Jest์์ ๊ธฐ๋ฅ ํ ์คํธ๋ฅผ ์งํํ๋ฏ์ด ๋ ๋๋ง์ด ์๋ ํจ์์ฒ๋ผ ๋งค๊ฐ๋ณ์์ ๋ฐ๋ฅธ ๊ฒฐ๊ณผ๊ฐ, ์ฆ ๋์ผํ ๋งค๊ฐ๋ณ์์ ๋ํด์ ๋์ผํ ๊ฒฐ๊ณผ๊ฐ์ ๋์ถํ๋์ง๋ฅผ ํ ์คํธํฉ๋๋ค.
๋ฐ๋ผ์, useInput ํ ์ ์ธ์ ์์ด ํธ์ถํ์ ๋ value๋ null์ธ ์ํ๊ฐ์ ์ง๋๊ณ , handler ํจ์๋ฅผ ํธ์ถํ ๊ฒฝ์ฐ, value์ ์ํ๊ฐ์ handler์ ์ธ์๋ก ๋ค์ด์จ ๊ฐ์ ์ํด ๋ณํ๊ฐ ์ด๋ฃจ์ด์ง ๊ฒ์ ์ฐ๋ฆฌ๋ ๊ธฐ๋ํ ์ ์๊ณ ๋ค์๊ณผ ๊ฐ์ด ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
describe('useInput', () => {
it('update value when handler is called', () => {
const { result } = renderHook(() => useInput())
expect(result.current[0]).toBe(null)
act(() => result.current[1]({ target: { value: 'Maeng Input Test' } }))
expect(result.current[0]).toBe('Maeng Input Test')
})
})renderHook()ํจ์์ React Hook ํจ์๋ฅผ ํธ์ถํ๋ ์ฝ๋๋ฅผ ์ธ์๋ก ๋๊ธฐ๋ฉดย result์์ฑ์ ๋ด๊ณ ์๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค. ์ดย result ๊ฐ์ฒด๋ย current ์์ฑ์ ๊ฐ๋๋ฐ, ์ด ์์ฑ์ ํตํด์ ํด๋น React Hook ํจ์์ ๋ฐํ๊ฐ์ ์ง์ ์ ๊ทผํ ์ ์์ต๋๋ค.
6. API ํธ์ถ ํ ์คํธ
์ด์ API ํธ์ถ์ ๊ฒฝ์ฐ ์ด๋ป๊ฒ ํ ์คํธ ํ๋์ง ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
API ํธ์ถ ํ ์คํธ์ ๊ฒฝ์ฐ๋ Custom Hook ํ ์คํธ์ ๋น์ทํ๊ฒ ํด๋น ํ ์ ํธ์ถ ํ์์๋, ์๋ฒ๋ก API ์์ฒญ์ ๋ณด๋ด๊ณ , ์ฌ๋ฐ๋ฅธ ์๋ต์ ๋ฐ๋์ง ํ ์คํธ๋ฅผ ์งํํ๋ฉด ๋๊ฒ ์ต๋๋ค.
์ ์ ๊ฒฝ์ฐ, API ํธ์ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก Axios๋ฅผ ์ฌ์ฉํ์๊ณ , ์๋ฒ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก React-query๋ฅผ ์ด์ฉํ์์ต๋๋ค. ํด๋น ํฌ์คํธ๋ ํ
์คํธ ์ฝ๋ ์์ฑ์ ๋ํ ๊ธ์ด๋ฏ๋ก Axios๋ React-query์ ๋ํ ์ค๋ช
์ ์๋ตํ๋๋ก ํ๊ฒ ์ต๋๋ค.
๋จผ์ , ๊ฐ๋จํ๊ฒ React-query๋ฅผ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฐ๋จํ ์ปค์คํ ํ ์ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
const useFetchData = () => {
const { data, isLoading, error } = useQuery('/data', async () => {
const response = await API.get(`/data`)
return response.data
})
return { data, isLoading, error }
}useFetchData ํ
์ API๋ฅผ ์์ฒญํ๊ณ ์๋ต๊ฐ์ data์ ๋ด์ { data, isLoading, error } ๊ฐ์ฒด๋ฅผ ๋ฆฌํดํฉ๋๋ค.
๋ฐ๋ผ์, ์ฐ๋ฆฌ๋ useFetchData ํ ์ ํธ์ถํ์ ๋, ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ํ ์ ์์ต๋๋ค.
// ๋ฐ์ดํฐ ์์ฒญ ์๋ฃ ์
{
data: undefined,
isLoading: true,
error: null
}
// ์ ์์ ์ผ๋ก ๋ฐ์ดํฐ ์์ฒญ ์๋ฃ
{
data: { name: 'Maeng' },
isLoading: false,
error: null
}์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
describe('useFetchData', () => {
it('fetch data', async () => {
const { result, waitFor } = renderHook(() => useFetchData(), {
wrapper: createQueryWrapper(),
})
await waitFor(() => !result.current.isLoading)
expect(result.current.data?.name).not.toBe(undefined)
})
})React-query๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ๋ณ๋์ Wrapper ์ปดํฌ๋ํธ๊ฐ ํ์ํฉ๋๋ค. ๋ฐ๋ผ์ createQueryWrapper ๋ผ๋ ํจ์๋ฅผ ๋ง๋ค์ด React-query๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก Wrapper ์ปดํฌ๋ํธ ์์ฑํด์ฃผ์์ต๋๋ค.
// React-query๋ฅผ ์ํ Wrapper ์ปดํฌ๋ํธ ์์ฑ
const createQueryWrapper = () => {
const testQueryClient = createTestQueryClient()
const Wrapper = ({ children }: { children }) => (
<QueryClientProvider client={testQueryClient}>{children}</QueryClientProvider>
)
return Wrapper
}ํ์ง๋ง ์์ฒ๋ผ API ํธ์ถ ํ ์คํธ ์งํ์ ํ๋ฉด์, ๋ช๊ฐ์ง ๋ถํธ์ฌํญ์ ๊ฒช๊ฒ ๋์์ต๋๋ค.
์ฒซ ๋ฒ์งธ๋ API์ ์๋ต์ ๋ด๋ ค์ฃผ๋ ์๋ฒ๊ฐ ์ ๋๋ก ๋์ํ์ง ์์ ๊ฒฝ์ฐ, ๋น์ฐํ๊ฒ๋ ์คํจ ๋ฉ์์ง๋ฅผ ๋ ธ์ถํ๊ฒ ๋ฉ๋๋ค. ๋ง์ฝ ๋ฐฑ์๋๊ฐ ๊ตฌํ์ด ์์ง ์๋ ์ํ๋ก ํ๋ก ํธ ์์ ์ ํ๋ค๋ฉด, ๊ณ์ํด์ ํ ์คํธ ์ผ์ด์ค์์ ์คํจ๊ฐ ๋ฐ์ํ ๊ฒ์ ๋๋ค.
๋ ๋ฒ์งธ๋ API๊ฐ DB์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์ ์์์๋ ๋จ์ํ data๊ฐ undefined๊ฐ ์๋์ ํ
์คํธํ์์ง๋ง, ์ข ๋ ์์ธํ๊ฒ data ๊ฐ์ ๋ํด ํ
์คํธ๋ฅผ ์งํํ ๊ฒฝ์ฐ DB๊ฐ ์์ ๋จ์ ๋ฐ๋ผ ์๋ต๊ฐ์ ๋งค๋ฒ ๋ฐ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ์ด ๋ํ ํ
์คํธ ์ผ์ด์ค์์ ์คํจ๊ฐ ๋ฐ์ํ ๊ฒ์
๋๋ค.
์ธ ๋ฒ์งธ๋ CORS ์ฒ๋ฆฌ๋ฅผ ์ถ๊ฐํด์ฃผ์ด์ผํ๋ ๋ฒ๊ฑฐ๋ก์(?)์ด ์์์ต๋๋ค. ์ ๊ฐ ๊ตฌํํ ์๋ฒ์ ๊ฒฝ์ฐ, Access-Control-Allow-Origin ์, React์ ๊ฒฝ์ฐ ํฌํธ ๋ฒํธ๋ฅผ 3000๋ฒ์ ์ฌ์ฉํ๊ธฐ์ 3000๋ฒ๋ง ํ์ฉํด์ฃผ์๋๋ CORS์๋ฌ๊ฐ ๋ฐ์ํ์ฌ ํ์ธํ ๊ฒฐ๊ณผ, ํ
์คํธ ์ฝ๋์ ๊ฒฝ์ฐ, API ํธ์ถ ์ 80ํฌํธ๋ฅผ ์ด์ฉํด์ ์์ฒญํ๊ณ ์์ด, ๋ง์ฝ API ์๋ฒ์์ 3000๋ฒ ํฌํธ๋ง์ ํ์ฉํ๊ณ ์๋ค๋ฉด 80ํฌํธ๋ ํ์ฉํ๋๋ก ์ถ๊ฐ์ ์ธ ์์
์ด ํ์ํ์ต๋๋ค.
๋ฐ๋ผ์, ์ด๋ป๊ฒ ํด๊ฒฐํ๋ฉด ์ข์๊น์ ๋ํด์ ๊ณ ๋ฏผํ๋ ์ค, axios-mock-adapter ์ ๋ํด ์๊ฒ ๋์์ต๋๋ค.
axios-mock-adapter ๋ฅผ ํ์ฉํ๋ฉด, API ์์ฒญ๊ณผ ์๋ต์ Mocking ํ ์ ์์ต๋๋ค. ๋ฐ๋ผ์ API ์์ฒญ๊ณผ ์๋ต๊ฐ์ ํ
์คํธ ์ฝ๋ ๋ด์์ ์กฐ์ํ ์ ์์ต๋๋ค.
๋ค์์ axios-mock-adapter ๋ฅผ ํ์ฉํด์ ํ
์คํธ ์ฝ๋๋ฅผ ์ฌ์์ฑํด๋ณด์์ต๋๋ค.
describe('useFetchData', () => {
it('fetch data', async () => {
mock.onGet('/data').reply(200, { name: 'Maeng' })
const { result, waitFor } = renderHook(() => useGetBankList(), {
wrapper: createQueryWrapper(),
})
await waitFor(() => !result.current.isLoading)
expect(result.current.data.name).toBe('Maeng')
})
})์ด์ฒ๋ผ axios-mock-adapter ์ ํ์ฉํ๋ฉด ์ง์ API ์์ฒญ์ ๋ณด๋ด์ง ์๊ณ ๋ ์์๋๋ ์๋ต ์ํ์ ๊ฐ์ ๋ด๋ ค์ฃผ๋๋ก API๋ฅผ ๋ชจํนํ์ฌ ํ
์คํธ๋ฅผ ์งํํ ์ ์์ต๋๋ค.
7. ๋ผ์ฐํ ํ ์คํธ
๋ค์์ผ๋ก, ์ปดํฌ๋ํธ ๋๋ ๋ฆฌ์กํธ ํ ์์ ๋ฐ์ํ๋ ๋ผ์ฐํ ์ ์ด๋ป๊ฒ ํ ์คํธํ๋์ง ์์๋ณด๊ฒ ์ต๋๋ค. ๋๊ฐ React์์๋ React-router-dom ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํด์ ๋ผ์ฐํ ์ค์ ์ ํ๋๋ฐ์. ์ต๊ทผ์ V6๊ฐ ๋ฆด๋ฆฌ์ฆ๋์ด ์ ๋ ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ์์ต๋๋ค.
๋จผ์ , ๋ค์๊ณผ ๊ฐ์ด ๋ผ์ฐํ ์ด ๋ฐ์ํ๋ ๊ฐ๋จํ ์ปดํฌ๋ํธ๋ฅผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
const Button = () => {
const navigate = useNavigate()
const onClick = () => {
navigate('/')
}
return <button onClick={onClick}>๋ฒํผ</button>
}์, ๋ผ์ฐํ
์ด ๋ฐ์ํ๋ ์ปดํฌ๋ํธ ์
๋๋ค. ์๊น๋ ๋ง์๋๋ ธ๋ค์ํผ ์ปดํฌ๋ํธ ํ
์คํธ๋ ์ฌ์ฉ์ ์
์ฅ์์ ์๊ฐํ์ฌ ํ
์คํธ๋ฅผ ์งํํฉ๋๋ค. ๋ฐ๋ผ์ ์ฐ๋ฆฌ๊ฐ ๊ธฐ๋ํ๋ ํด๋น ์ปดํฌ๋ํธ์ ์๋๋ฆฌ์ค๋ "๋ฒํผ" ์ด๋ผ๋ ํ
์คํธ๋ฅผ ์ง๋ ๋ฒํผ์ ํด๋ฆญํ๋ฉด โ/โ๋ก ๋ผ์ฐํ
๋๋ ๊ฒ์
๋๋ค.
์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ์ ์์ ์ฐ๋ฆฌ๋ react-router-dom ๋์์ ๋ํด์ ์๊ฐํด ๋ณด์์ผ ํฉ๋๋ค. ์ข์ ์ react-query ํ ์คํธ๋ฅผ ํ ๋, Wrapper ์ปดํฌ๋ํธ๊ฐ ํ์ํ์๋ฏ์ด, react-router-dom ์ด ๋์ํ๊ธฐ ์ํด Wrapper ์ปดํฌ๋ํธ๊ฐ ํ์ํฉ๋๋ค.
๋ฐ๋ผ์ React-router-dom์ ์ํด ํ
์คํธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ ๋ ๋๋งํด์ค ์ ์๋ renderWithRouter ํจ์๋ฅผ ์์ฑํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
// React-router-dom์ ์ํ Wrapping
const renderWithRouter = (children) => {
return {
...render(<MemoryRouter>{children}</MemoryRouter>
)
};๋ณดํต React์์๋ React-router-dom์์ ์ ๊ณตํ๋ App.js์ BrowserRouter ๋๋ HashRotuer ๋ฅผ ํตํด ์ปดํฌ๋ํธ๋ค์ ๊ฐ์ธ ๋ผ์ฐํ
์ ๊ตฌํํ๋๋ฐ, MemoryRouter๋ <BrowserHistory> ๋ฐย <HashHistory> ์๋ ๋ฌ๋ฆฌ ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ก ์คํ๊ณผ ๊ฐ์ ์ธ๋ถ ์์ค์ ์ฐ๊ฒฐ๋์ง ์์ต๋๋ค.ย ๋ฐ๋ผ์ ํ
์คํธ์ ๊ฐ์ด ๊ธฐ๋ก ์คํ์ ์์ ํ ์ ์ดํด์ผ ํ๋ ์๋๋ฆฌ์ค์ ์ด์์ ์ด๋ผ๊ณ ํฉ๋๋ค.
์ด์ ๋ผ์ฐํ ์ ํ ์คํธํ๊ธฐ ์ํ ์ค๋น๋ฅผ ๋ง์ณค์ผ๋, ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํด์ ํ ์คํธ๋ฅผ ์งํํ์์ต๋๋ค.
// useNavigate ๋ชจํน
const mockedNavigator = jest.fn()
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: () => mockedNavigator,
}))
describe('Button', () => {
it('renders Button', () => {
const { getByText } = renderWithRouter(<Button />)
expect(getByText('๋ฒํผ')).toBeInTheDocument()
fireEvent.click(button)
expect(mockedNavigator).toBeCalledTimes(1)
})
})ํด๋น ํ ์คํธ ์ฝ๋๋ฅผ ์คํ์ํค๋ฉด ์ ์์ ์ผ๋ก ํต๊ณผํ๋ ๊ฒ์ ํ์ธํ ์ ์์ง๋ง, ์ ๋ ์์ง ๋ง์กฑํ์ง ๋ชปํ์ต๋๋ค.
์ ํํ โ/โ๋ก ๋ผ์ฐํ ๋๋์ง๊น์ง ํ ์คํธ๋ฅผ ์งํํ์ฌ ์ข๋ ์๋ฒฝํ ํ ์คํธ ์ฝ๋ ์์ฑํ๊ณ ์ถ์์ต๋๋ค.
ํ์ง๋ง ๊ตฌ์ฑ ์์๊ฐ ์ผ๋ถ ์์
์์ ๊ฒฝ๋ก๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒฝ์ฐ MemoryRouter๋ฅผ ์ฌ์ฉย ํ์ฌย history์ ๋ณ๊ฒฝ ์ฌํญ์ ์ก์ธ์คํ ์ ์๊ธฐ ๋๋ฌธ์ย MemoryRouterย ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ง์ผ๋ก๋ ์ถฉ๋ถํ์ง ์๋ค๋ ์ฌ์ค์ ์๊ฒ๋์๊ณ , history์ ๋ํด ์ก์ธ์คํ ์ ์๋ HistoryRouter ๋ฅผ ํ์ฉํ๊ณ ์ ํ์์ต๋๋ค.
๋ค์์ HistoryRouter๋ฅผ ํ์ฉํด์ renderWithRouter๋ฅผ ์ฌ๊ตฌํํ ์ฝ๋์
๋๋ค.
const renderWithRouter = (renderComponent, route) => {
const history = createMemoryHistory();
if (route) {
history.push(route);
}
return {
...render(
<HistoryRouter history={history}>
<Routes>{renderComponent()}</Routes>
</HistoryRouter>
),
history,
};์ด์ ๋ history์ ๋ณ๊ฒฝ์ฌํญ์ ๋ํด ์์ธ์คํ ์ ์์ต๋๋ค.
๋ค์ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
describe('Button', () => {
it('renders Button', () => {
const { getByText, history } = renderWithRouter(
() => (
<>
<Route path="/test" element={<Button />} />
<Route path="/" element={<div />} />
</>
),
'/test',
)
expect(getByText('๋ฒํผ')).toBeInTheDocument()
fireEvent.click(button)
await waitFor(() => {
expect(history.location.pathname).toEqual('/')
})
})
})์ด์ ์ฝ๋์ ๋ฌ๋ฆฌ useNavigate ๋ชจํนํ์ง ์์๋ ๋๋ฉฐ, history ๊ฐ์ฒด์ ์ก์ธ์คํ์ฌ ์ ํํ๊ฒ โ/โ ๋ก ๋ผ์ฐํ ๋๋์ง ํ ์คํธํ ์ ์๊ฒ ๋์์ต๋๋ค.
8. ์ฌํ ํ ์คํธ
์ด์ ๋ ์์์ ํ์ตํ ๋ด์ฉ์ ๋ฐํ์ผ๋ก ๋ณตํฉ์ ์ธ ํ ์คํธ๋ฅผ ์งํํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๋ค์์ ์๋ฒ๋ก ๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์ด๋ฅผ ์ ๋ฐ์ดํธ ํ๋ ํ์ด์ง ์ปดํฌ๋ํธ ์ ๋๋ค. ์ปดํฌ๋ํธ ์์ธ ์ฝ๋๋ ์๋ตํ๊ณ ํ ์คํธ ์ฝ๋ ์์ฃผ๋ก ์์ฑํ๊ณ ์ค๋ช ๋๋ฆฌ๋๋ก ํ๊ฒ ์ต๋๋ค.
const UpdatePage = () => {
const { id } = useParams()
const navigate = useNavigate()
const { data, error, isLoading, isError } = useFetchData(id)
const { mutateAsync, isLoading: isMutating } = useUpdateData()
const onFormSubmit = async (formData) => {
await mutateAsync({ ...formData, id })
navigate('/')
}
if (isLoading) {
return <div data-testid="loader">Loading</div>
}
if (isError) {
return <div>Error: {error.message}</div>
}
return (
<div>
<h1>Update Data</h1>
<DataForm defaultValues={data} onFormSubmit={onFormSubmit} isLoading={isMutating} />
</div>
)
}์ ๋ฐ์ดํธ ํ์ด์ง์ ๋ํด์ ๊ฐ๋ตํ๊ฒ ์ค๋ช ๋๋ฆฌ์๋ฉด, ํ์ฌ url๋ก ๋ถํฐ id ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ ธ์ ํด๋น id์ ํด๋นํ๋ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๊ณ ํผ ์ปดํฌ๋ํธ์์๋ ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐํ์ผ๋ก ๊ธฐ๋ณธ๊ฐ์ด ์ธํ ๋๋ฉฐ, ์ด๋ฅผ ์์ ํ๊ณ ์ ๋ฐ์ดํธํ ๊ฒฝ์ฐ, ๋ฃจํธ(โ/โ)๋ก ๋ผ์ฐํ ๋๋ ํ์ด์ง ์ ๋๋ค.
์ ๋ฐ์ดํธ ํ์ด์ง์์ ์ฐ๋ฆฌ๊ฐ ๊ธฐ๋ํ๋ ๊ฒฐ๊ณผ๋ค์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- url ํ๋ผ๋ฏธํฐ๋ก ์ฃผ์ด์ง id๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ํจ์๊ฐ ํธ์ถ๋๋ค.
- ๋ก๋ฉ์ค์๋ ๋ก๋ฉ ํ๋ฉด์ด ๋ ธ์ถ๋๋ค.
- ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ์๋ฌ๋ฌธ๊ตฌ๊ฐ ๋ ธ์ถ๋๋ค.
- ๋ฐ์ดํฐ๊ฐ ์ ์์ ์ผ๋ก ์กฐํ๋์์ ๊ฒฝ์ฐ,
"Update Data"๋ฌธ๊ตฌ๊ฐ ๋ ธ์ถ๋๊ณ DataForm ์ปดํฌ๋ํธ์ defaultValue๊ฐ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ์ ๋ฌ๋๋ค. - ํผ์์ ์ ์ถ์ด ์ ์์ ์ผ๋ก ์ด๋ฃจ์ด์ง๋ฉด, ๋ฃจํธ(โ/โ)๋ก ํ์ด์ง ์ด๋์ด ๋ฐ์ํ๋ค.
์์ ๊ธฐ๋์ฌํญ๋ค์ ๊ธฐ๋ฐ์ผ๋ก ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ์๋์ ๊ฐ์ต๋๋ค.
jest.mock('./useFetchData', () => ({
useFetchData: jest.fn(),
}))
jest.mock('./useUpdateData', () => ({
useUpdateData: jest.fn(),
}))
jest.mock('./DataForm', () => ({
DataForm: jest.fn(),
}))
describe('UpdatePage', () => {
beforeEach(() => {
useFetchData.mockImplementation(() => ({}))
useUpdateData.mockImplementation(() => ({}))
DataForm.mockImplementation(() => null)
})
it('fetches the data for the given id', () => {
renderWithRouter(() => <Route path=":id" element={<UpdatePage />} />, '/test-id')
expect(useFetchData).toHaveBeenCalledWith('test-id')
})
describe('while loading', () => {
it('renders a loader', () => {
useFetchData.mockImplementation(() => ({
isLoading: true,
}))
const { getByTestId } = renderWithRouter(
() => <Route path=":id" element={<UpdatePage />} />,
'/test-id',
)
expect(getByTestId('loader')).toBeTruthy()
})
})
describe('with an error', () => {
it('renders an error message', () => {
useFetchData.mockImplementation(() => ({
isError: true,
error: { message: 'Something went wrong' },
}))
const { container } = renderWithRouter(
() => <Route path=":id" element={<UpdatePage />} />,
'/test-id',
)
expect(container.innerHTML).toMatch('Error: Something went wrong')
})
})
describe('with data', () => {
it('renders the update page title and the data form', () => {
useFetchData.mockImplementation(() => ({
data: { foo: 'bar' },
}))
const { container } = renderWithRouter(
() => <Route path=":id" element={<UpdatePage />} />,
'/test-id',
)
expect(container.innerHTML).toMatch('Update Data')
expect(DataForm).toBeCalledWith(
expect.objectContaining({
defaultValues: { foo: 'bar' },
}),
{},
)
})
describe('on data form submit', () => {
it('updates the data and navigates to the root page', async () => {
DataForm.mockImplementation(({ onFormSubmit }) => (
<button onClick={() => onFormSubmit({ foo: 'bar' })}>Submit</button>
))
const mutateAsync = jest.fn()
useUpdateData.mockImplementation(() => ({ mutateAsync }))
const { getByText, history } = renderWithRouter(
() => (
<>
<Route path=":id" element={<UpdatePage />} />
<Route path="/" element={<div />} />
</>
),
'/test-id',
)
fireEvent.click(getByText('Submit'))
expect(mutateAsync).toHaveBeenCalledWith({
foo: 'bar',
id: 'test-id',
})
await waitFor(() => {
expect(history.location.pathname).toEqual('/')
})
})
})
})
})9. ์ฐธ๊ณ
Mocking
ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ ๋ ์์กด์ ์ธ ๋ถ๋ถ์ ์ง์ ์์ฑํ๊ธฐ๊ฐ ๋ถ๋ด์ค๋ฝ๊ธฐ ๋๋ฌธ์ ๋ชจํน์ ๋ง์ด ์ฌ์ฉํ๋๋ฐ, ํ ์คํธ ํ๊ณ ์ถ์ ๊ธฐ๋ฅ์ด ๋ค๋ฅธ ๊ธฐ๋ฅ๋ค๊ณผ ์ฎ์ฌ์์ ๊ฒฝ์ฐ, ์ ํํ ํ ์คํธ๋ฅผ ํ๊ธฐ ํ๋ค๊ธฐ ๋๋ฌธ์ ํด๋น ์ฝ๋๊ฐ ์์กดํ๋ ๋ถ๋ถ์ ๊ฐ์ง๋ก ๋์ฒดํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
jest.fn()
Jest๋ ๊ฐ์ง ํจ์(mock functiton)๋ฅผ ์์ฑํ ์ ์๋๋กย jest.fn()ย ํจ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ด๋ฅผ ์ด์ฉํด์ ์ผํ์ฑ ํ ์คํธ์ฉ์ผ๋ก์ ๋ด๋ถ์ ํจ์๋ฅผ ์ง์ง๊ฐ์ด ๊ตฌ๋ํด์ ์ฝ๋๋ฅผ ๊ตฌ๋ ์ํฌ ์ ์์ต๋๋ค.
jest.mock()
jest.fn()์ ๊ฐ๋ณ์ ์ผ๋ก ํ๋ํ๋ ๋ชจํน ์ฒ๋ฆฌ๋ฅผ ํด์ค๋ ์ฌ์ฉํ๋ฉฐ, jest.mock()์ ๊ทธ๋ฃน์ผ๋ก ํ๋ฒ์ ๋ชจํน ์ฒ๋ฆฌ๋ฅผ ํด์ค๋ ์ฌ์ฉํฉ๋๋ค.
Jest API
๋ค์์ Jest๋ก ๋จ์ ํ ์คํธ ์ฝ๋๋ฅผ ๊ตฌํํ ๋ ์์ฃผ ์ฌ์ฉ๋๋ API ๋ชฉ๋ก์ ๋๋ค.
describe
์ฌ๋ฌ ๊ฐ์ ํ ์คํธ ์ผ์ด์ค๋ฅผ ๋ฌถ์๋ ์ฌ์ฉํ๋ฉฐ, ์์ ๋จ์์ ํ ์คํธ ์ฝ๋๋ฅผ ๊ทธ๋ฃนํํ ์ ์๋ค.
it๊ณผ test
it๊ณผ test ๋ ๊ฐ์ง ๋ชจ๋ ํ๋์ ํ ์คํธ ์ผ์ด์ค๋ฅผ ์๋ฏธํ๋ฉฐ ์์ ํ ๋์ผํ ๊ธฐ๋ฅ์ ํฉ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋กย it๋ ์ ๋ณ์นญย test์ด๋ฏ๋ก ๊ธฐ๋ฅ์ ์ผ๋ก ๋์ผํฉ๋๋ค.
expect
expect()์๋ ์ฃผ๋ก ํ
์คํธ ์
๋ ฅ ๊ฐ ๋๋ ๊ธฐ๋ ๊ฐ์ ๋ฃ์ต๋๋ค
toBe
ํ ์คํธ์ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ๋ API. ํ ์คํธ์ ์์ ๊ฒฐ๊ณผ ๊ฐ์ ๋ฃ์ต๋๋ค.
beforeEach
ํ
์คํธ ํ์ผ์ ๊ฐ ํ
์คํธ ์ฝ๋๊ฐ ๋๊ธฐ ์ ์ ์ํํ ๋ก์ง์ ๋ฃ๋ API. ํ
์คํธ ์ผ์ด์ค๋ง๋ค ๋ฐ๋ณต๋๋ ๋ก์ง์ ๋ฃ๊ธฐ์ ์ ํฉํฉ๋๋ค.
RTL์ ๋ค์ํ Query
RTL ์๋ ์๋์ ๊ฐ์ด Variant์ Query๊ฐ ์กด์ฌํ๋ฉฐ, ์ด ๋์ ์กฐํฉ์ ํตํด ํน์ DOM์ ์ ํํ ์ ์์ต๋๋ค.
Variant
getBy
getBy*ย ๋ก ์์ํ๋ ์ฟผ๋ฆฌ๋ ์กฐ๊ฑด์ ์ผ์นํ๋ DOM ์๋ฆฌ๋จผํธ ํ๋๋ฅผ ์ ํํฉ๋๋ค. ๋ง์ฝ์ ์์ผ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค.
getAllBy
getAllBy*ย ๋ก ์์ํ๋ ์ฟผ๋ฆฌ๋ ์กฐ๊ฑด์ ์ผ์นํ๋ DOM ์๋ฆฌ๋จผํธ ์ฌ๋ฌ๊ฐ๋ฅผ ์ ํํฉ๋๋ค. ๋ง์ฝ์ ํ๋๋ ์์ผ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค.
queryBy
queryBy*ย ๋ก ์์ํ๋ ์ฟผ๋ฆฌ๋ ์กฐ๊ฑด์ ์ผ์นํ๋ DOM ์๋ฆฌ๋จผํธ ํ๋๋ฅผ ์ ํํฉ๋๋ค. ๋ง์ฝ์ ์กด์ฌํ์ง ์์๋ ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์์ต๋๋ค.
queryAllBy
queryAllBy*ย ๋ก ์์ํ๋ ์ฟผ๋ฆฌ๋ ์กฐ๊ฑด์ ์ผ์นํ๋ DOM ์๋ฆฌ๋จผํธ ์ฌ๋ฌ๊ฐ๋ฅผ ์ ํํฉ๋๋ค. ๋ง์ฝ์ ์กด์ฌํ์ง ์์๋ ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์์ต๋๋ค.
findBy
findBy*ย ๋ก ์์ํ๋ ์ฟผ๋ฆฌ๋ ์กฐ๊ฑด์ ์ผ์นํ๋ DOM ์๋ฆฌ๋จผํธ ํ๋๊ฐ ๋ํ๋ ๋ ๊น์ง ๊ธฐ๋ค๋ ธ๋ค๊ฐ ํด๋น DOM ์ ์ ํํ๋ Promise ๋ฅผ ๋ฐํํฉ๋๋ค. ๊ธฐ๋ณธ timeout ์ธ 4500ms ์ดํ์๋ ๋ํ๋์ง ์์ผ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค.
findAllBy
findBy*ย ๋ก ์์ํ๋ ์ฟผ๋ฆฌ๋ ์กฐ๊ฑด์ ์ผ์นํ๋ DOM ์๋ฆฌ๋จผํธ ์ฌ๋ฌ๊ฐ๊ฐ ๋ํ๋ ๋ ๊น์ง ๊ธฐ๋ค๋ ธ๋ค๊ฐ ํด๋น DOM ์ ์ ํํ๋ Promise ๋ฅผ ๋ฐํํฉ๋๋ค. ๊ธฐ๋ณธ timeout ์ธ 4500ms ์ดํ์๋ ๋ํ๋์ง ์์ผ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค.
Queries
ByLabelText
ByLabelTextย ๋ label ์ด ์๋ input ์ label ๋ด์ฉ์ผ๋ก input ์ ์ ํํฉ๋๋ค.
ByPlaceholderText
ByPlaceholderTextย ๋ placeholder ๊ฐ์ผ๋ก input ๋ฐ textarea ๋ฅผ ์ ํํฉ๋๋ค.
ByText
ByText๋ ์๋ฆฌ๋จผํธ๊ฐ ๊ฐ์ง๊ณ ์๋ ํ
์คํธ ๊ฐ์ผ๋ก DOM ์ ์ ํํฉ๋๋ค.
์ฐธ๊ณ ๋ก, ํ ์คํธ ๊ฐ์ ์ ๊ท์์ ๋ฃ์ด๋ ์๋ํฉ๋๋ค.
ByAltText
ByAltTextย ๋ย altย ์์ฑ์ ๊ฐ์ง๊ณ ์๋ ์๋ฆฌ๋จผํธ (์ฃผ๋กย img) ๋ฅผ ์ ํํฉ๋๋ค.
ByTitle
ByTitleย ์ย titleย ์์ฑ์ ๊ฐ์ง๊ณ ์๋ DOM ํน์ย titleย ์๋ฆฌ๋จผํธ๋ฅผ ์ง๋๊ณ ์๋ SVG ๋ฅผ ์ ํ ํ ๋ ์ฌ์ฉํฉ๋๋ค.