홈으로 돌아가기

CI에서 Playwright 테스트 안정화

이 기사는 Dependency Projects, web-first asserts 및 적절한 로케이터 선택을 사용하여 CI에서 Playwright 테스트를 안정화하는 방법론을 설명합니다. 엔터프라이즈 프로젝트를 위한 구성 및 코드 예제가 제공됩니다.

Playwright: Dependency Projects로 CI에서 플레이키 제거
Advertisement 728x90

CI에서 플레이라이트 테스트 안정화: 의존성 프로젝트와 웹 우선 검증

플레이라이트의 체인형 의존성은 불안정한 환경에서 테스트 실행을 방지해 CI 자원을 절약하고, 오류 발생률을 줄입니다.

먼저 API를 통해 인증을 수행하고, 저장 상태를 유지합니다. 이후 건강 상태 확인을 통해 API 가용성을 검증합니다. 이 과정이 완료된 후에야 메인 UI 테스트가 실행됩니다.

글로벌 세팅보다 우수한 점:

Google AdInline article slot
  • 네트워크 요청, 스크린샷, 콘솔 로그 포함 전부의 트레이스 기록 가능.
  • 즉시 실패: 설정 단계에서 실패하면 의존 프로젝트는 즉시 중단됩니다.
  • 격리성: npx playwright test --project=auth-setup로 개별 단계를 독립적으로 테스트할 수 있습니다.

playwright.config.ts 내 구성 예시:

export default defineConfig({
  projects: [
    {
      name: 'auth-setup',
      testMatch: /.*\.auth\.setup\.ts/,
    },
    {
      name: 'healthcheck',
      testMatch: /.*\.health\.setup\.ts/,
      dependencies: ['auth-setup'],
    },
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
      dependencies: ['healthcheck'],
    },
  ],
});

API 기반 인증은 설정 시간을 극도로 단축합니다: UI 로그인에 2~5초가 걸리는 반면, POST 요청은 50~100ms면 충분합니다.

auth-setup 테스트 예시:

Google AdInline article slot
test('setup', async ({ request }) => {
  await request.post('/api/login', { data: { user, pass } });
  await request.storageState({ path: '.auth/user.json' });
});

의존성 그래프는 병렬 테스트 실행 시 레이스 조건을 제거합니다.

로케이터를 현명하게 선택하기

로케이터 선택은 UI 변경에 대한 테스트 강건성에 직접적인 영향을 미칩니다. 목적에 따라 분리하세요: 액션에는 getByTestId, 검증에는 getByRole 또는 getByLabel을 사용하세요.

최적의 실천 방법:

Google AdInline article slot
  • 액션(클릭, 입력): page.getByTestId('add-to-cart-button').click() — 텍스트나 스타일과 무관합니다.
  • 텍스트/상태 검증: expect(page.getByRole('heading')).toHaveText('내 계정').
  • 입력 필드: page.getByLabel('이메일').fill('[email protected]').
  • 카운터: expect(locator).toHaveText('3').

CSS 선택자는 최후의 수단으로만 사용하세요. 팀원들에게 올바른 로케이터 사용법을 교육하면 스케일링 시 10배 이상의 효과를 얻을 수 있습니다.

웹 우선 검증 vs 불안정성

await loc.isVisible() 같은 스냅샷 검사는 레이스 조건에 취약합니다. 폴링 기반의 웹 우선 검증은 안정성을 보장합니다.

검증 비교표:

| 검사 항목 | 스냅샷(대기 없음) | 웹 우선(폴링) |

|----------|------------------|--------------|

| 가시성 | await loc.isVisible() | expect(loc).toBeVisible() |

| 텍스트 | await loc.textContent() | expect(loc).toHaveText('문자열') |

| 개수 | await loc.count() | expect(loc).toHaveCount(3) |

| 속성 | await loc.getAttribute('href') | expect(loc).toHaveAttribute('href', '/path') |

| 체크박스 | await loc.isChecked() | expect(loc).toBeChecked() |

| 상태 | await loc.isEnabled() | expect(loc).toBeEnabled() |

폴링은 100ms 간격으로 재시도하며, 일시적인 UI 깜빡임을 무시합니다.

하이드레이션 및 force: true

SSR/SSG 앱에서는 서버 렌더링 마크업은 존재하지만, JS 핸들러가 아직 연결되지 않은 경우 하이드레이션 불일치가 발생합니다.

하이드레이션 마커 대기:

  • await page.waitForSelector('.hydrated', { state: 'attached' })
  • await expect(page.locator('#global-loader')).toBeHidden()

force: true는 동작 가능성 검사를 비활성화합니다(가시성, 안정성, 활성화 여부, 이벤트 수신). 이로 인해 테스트는 순수 DOM 스크립트처럼 작동합니다. 파일 입력과 같은 특수 사례에만 적합하며, 반드시 주석을 함께 작성해야 합니다.

핵심 요약

  • 의존성 프로젝트는 의존성 그래프를 구축해 준비되지 않은 환경에서 테스트가 실행되는 것을 막습니다.
  • 웹 우선 검증은 폴링을 통해 레이스 조건으로 인한 불안정성의 80%를 제거합니다.
  • 로케이터 역할 분리는 UI 리팩토링에 대한 강건성을 높입니다.
  • API 기반 인증은 UI 로그인보다 설정 속도를 20~50배 빠르게 합니다.
  • 트레이스는 스크린샷보다 CI에서 실패 원인을 파악하는 데 더 풍부한 맥락을 제공합니다.

— Editorial Team

Advertisement 728x90

다음 읽기