프론트엔드의 동작에는 서로 기반하여 빌드되는 흐름이 있기 때문에
단언을 여러개 선언해도 괜찮다 - 코드 중복 여부보다 알아보기 쉽고, 잘 동작하는지가 중요
테스트는 언제나 무언가를 렌더링 하면서 시작한다
test("버튼이 빨간색으로 시작되어야 한다.", () => {
render(<App />)
const buttonElement = screen.getByRole('button', {name: /blue/i})
expect(buttonElement).toHaveClass('red')
})
App 내에 만들 버튼을 테스트할 코드 작성 후 red test
아직 element 를 생성하지 않아 찾을 수 없다는 에러 발생
테스트 코드 내부가 비어있다면 통과한 것으로 나온다
function App() {
return <div>
<button className={'red'}>Change to blue</button>
</div>
}
테스트 할 컴포넌트 작성 후 테스트에 통과되면
blue test 완료
import { render, screen, fireEvent } from "@testing-library/react";
import {logRoles} from "@testing-library/react";
import App from "./App.jsx";
test("버튼이 빨간색으로 시작되어야 한다.", () => {
const {container} = render(<App />)
logRoles(container)
const buttonElement = screen.getByRole('button', {name: /blue/i})
expect(buttonElement).toHaveClass('red')
})
HTMLElement 를 필수로 받아 text 에서 로그를 출력한다
만약 이미 작성된 컴포넌트가 있고, 테스트를 작성해야 할 때 유용하다
간단한 클릭 등의 이벤트에는 fireEvent 를 사용
복잡한 인터랙션에는 userEvent 를 사용할 수 있다
위 예제처럼 class 를 사용하여 버튼 색상을 조절하는 것을 테스트한다면
실제로 css 파일 내에 class 가 있는지 확인해야 한다
css 를 직접 테스트 코드로 넣으면 조금 더 복잡해진다
jest 에서 css 를 해석하는 플러그인을 설치하여 진행할 수 있다
css 는 파싱도 느리고 테스트의 복잡성이 증대하기 때문에
테스트가 css 에 의존하지 않는 경우 config 에서 css 옵션을 false 로 주는 편이 좋다
toHaveStyle() 을 사용하면 css 를 가져와서 테스트를 할 수 있다
expect(buttonElement).toHaveStyle({ backgroundColor: 'blue'})
위와 같이 작성했다면 css blue 에 대한 색상을 찾지 못한다
css 값을 blue 로 줬더라도 테스트에서는 rgb 나 헥사코드 값을 줘야 찾을 수 있다
테스트 시에 색이 아니라 코드를 평가하는 것 같다
매칭되는 class name 에 정확하게 css 를 주고
클래스로 평가하는 편이 더 편리한 것 같다
TDD 에서 실제로 작동하는 앱을 확인할 순 없다
때문에 직접 눈으로 원하는 작업이 잘 완료되었는지 확인하려면
dev server 를 켜서 확인해야 한다
describe(): 테스트를 그룹화 할 수 있다
내부에서 여러개의 test() 작성 가능
간단한 구문의 경우 expect() 내에서 함수를 실행하라는데
잘모르겠다
describe() 로 그룹화한 테스트들 모두 각각의 테스트로 평가된다
jest 는 .test.js
로 파일을 찾아 테스트를 진행한다
요소가 사라지거나 화면에서 숨김 처리 되었을 때의 테스트 또한 인터랙션으로 fireEvent 를 사용할 수 있다
userEvent 가 조금 더 안전하고 현실적인 시뮬레이팅 방식이라고 한다
fireEvent 는 DOM 이벤트를 발생시키는데 컴퓨팅 이벤트를 발생하는 것이고
userEvent 는 이벤트 전체를 시뮬레이팅 한다
실제 사용자 인터랙션을 더 완벽히 시뮬레이팅 하는 것
일부 동작은 userEvent로 지원 하지 않는다
import userEvent from '@testing-library/user-event'
test('test name', async () => {
const user = userEvent.setup()
await user.click(element)
})
setup() 메서드를 통해 유저 인스턴스 생성 후
이벤트 종류를 선택하여 사용한다
유저 이벤트는 항상 promise 를 반환한다
test 콜백에 async 함수로 작성하는 것을 기억하자..
getBy 는 대상이 표시되지 않는 상태에는 이용할 수 없다
[All]
: 하나 이상의 매치를 기대할 경우 사용하여 매칭되는 전체의 배열을 얻을 수 있다
무엇으로 검색할지
화면을 보고있는 사람이든 아니든 모두에게 해당하는 쿼리를 작성하는 것이 좋다
시멘틱 쿼리는 브라우저마다 일관성이 떨어지기 때문에 사용 시 주의해야 한다
text id 를 사용하는 것은 최후의 방법으로 권장되지 않는다
mock service worker
service worker 를 통해 오프라인 상황을 지원한다
네트워크 호출을 가로채고 mocking 응답을 반환한다
브라우저나 노드 환경을 지원
코드 레벨이 아닌 네트워크 레벨에서 mocking 을 지원한다
간단한 앱에서는 모킹이 가능하지만 더 복잡한 앱에서는 MSW로 다양한 도구를 이용할 수 있다
npm install msw
요청을 처리하는 함수들로 호출된 URL 을 확인해서 모의 응답을 반환한다
handlers.js
import {http, HttpResponse} from "msw";
export const handlers = [
// rest api 를 사용하면 http / graphql 을 사용하면 graphql
http.get('https://localhost:3030/scoops', () => {
return HttpResponse.json([
{name: 'Chocolate', imagePath: '/images/chocolate.png'},
{name: 'Vanilla', imagePath: '/images/vanilla.png'},
])
})
]
const handler = handlers[0]
console.log(handler)
server.js
import {setupServer} from "msw/node";
import {handlers} from "./handlers";
export const server = setupServer(...handlers);
src/setupTest.js
import "@testing-library/jest-dom";
import {beforeAll, afterEach, afterAll} from "vitest";
import {server} from "./mocks/server.js";
beforeAll(() => { server.listen() });
// API mocking test 이전 서버 실행
afterEach(() => { server.resetHandlers() });
// 각 test 사이 핸들러 리셋
afterAll(() => { server.close() });
// 모든 test 완료 후 서버 종료
함수 테스트 시 반환값이
원시 타입이면 toBe()
객체 타입이면 toEqual()