005 - 테스팅
스캐폴드된 플러그인에는 이미 테스트가 준비되어 있어요. 테스트가 어떻게 동작하는지, 또 어떻게 확장하는지 차근차근 살펴볼게요.
출처: 문서
본문
스캐폴드된 플러그인에는 이미 테스트가 준비되어 있어요. 테스트가 어떻게 동작하는지, 또 어떻게 확장하는지 차근차근 살펴볼게요.
단위 테스트
Backstage 프론트엔드 플러그인은 테스트 러너로 Jest, 컴포넌트 렌더링에 React Testing Library, HTTP 요청 차단에 MSW(Mock Service Worker)를 사용해요.
TodoList 테스트
plugins/todo/src/components/TodoList/TodoList.test.tsx를 여세요. TodoList 컴포넌트는 프레젠테이션 컴포넌트라서 테스트가 간단해요. 데이터를 전달하고 렌더링되는지 확인하면 되거든요.
await renderInTestApp(<TodoList todos={todos} />);expect(screen.getByText('First task')).toBeInTheDocument();
@backstage/frontend-test-utils의 renderInTestApp은 컴포넌트 주변에 최소한의 Backstage 앱 컨텍스트를 구성해서, 컴포넌트가 의존할 수 있는 모든 표준 API를 제공해요.
TodoPage 테스트
plugins/todo/src/components/TodoPage/TodoPage.test.tsx를 여세요. TodoPage 컴포넌트는 백엔드에서 데이터를 가져오므로, 테스트는 MSW로 HTTP 요청을 차단해 테스트 데이터를 반환해요.
const server = setupServer();registerMswTestHooks(server);it('renders todos from the backend', async () => { server.use( http.get('*/api/todo/todos', () => HttpResponse.json({ items: [{ id: '1', title: 'Mocked task' /* ... */ }], }), ), ); await renderInTestApp(<TodoPage />); expect(await screen.findByText('Mocked task')).toBeInTheDocument();});
여기서 몇 가지 짚고 넘어갈게요.
-
registerMswTestHooks는 각 테스트 주변에서 MSW 서버를 설정하고 정리(teardown)해 줘서, 핸들러가 테스트 간에 새어 나가지 않아요. -
screen.findByText는 요소가 나타날 때까지 기다리는 프로미스를 반환해요. 이 덕분에 명시적인waitFor호출 없이도 데이터 가져오기의 비동기 특성을 처리할 수 있어요. -
URL 패턴
*/api/todo/todos는 호스트와 무관하게 일치해서, 테스트가 discovery API가 해석한 기본 URL에 의존하지 않게 해줘요.
테스트는 오류 케이스도 다뤄요. 백엔드가 500 상태를 반환하면 컴포넌트는 오류 패널을 표시하는 대신 예제 todo 항목을 렌더링해요.
테스트 실행하기
리포지토리 루트에서:
yarn test plugins/todo
추가 테스트 작성하기
플러그인에 기능을 추가할 때 같은 패턴을 따라가세요.
-
프레젠테이션 컴포넌트는 props를 전달하고 렌더링된 출력을 검증하세요.
-
데이터를 가져오는 컴포넌트는 MSW로 HTTP 응답을 목킹하세요.
-
비동기 검증에는
waitFor보다screen.findByText를 선호하세요. -
성공 경로와 오류 경로를 모두 테스트하세요.
통합 테스트
종단 간 검증을 위해 Playwright를 사용해 실제 브라우저에서 실행 중인 Backstage 인스턴스를 대상으로 플러그인을 테스트할 수 있어요. 통합 테스트는 단위 테스트로는 잡을 수 없는 문제(라우팅 문제, CSS 회귀, 전체 API 왕복 문제 등)를 잡아내요.
todo 페이지의 기본적인 Playwright 테스트는 이렇게 생겼을 거예요.
import { test, expect } from '@playwright/test';test('todo page shows the todo list', async ({ page }) => { await page.goto('/todo'); await expect(page.getByText('Welcome to todo!')).toBeVisible(); await expect(page.getByRole('table')).toBeVisible();});
통합 테스트는 중요한 사용자 흐름에서 가장 가치가 커요. 대부분의 컴포넌트 수준 검증은 위에서 설명한 단위 테스트 방식이 설정이 덜 들고 더 빠른 피드백을 주는 편이에요.