(준)공식 문서/Playwright

[ Playwright ] Playwright 입문 가이드: 테스트는 이렇게 써야 합니다

Je-chan 2025. 5. 14. 10:26

프론트엔드 개발을 하다 보면 “지금 버튼 눌렀을 때 진짜 동작하는지?”, “화면에 특정 요소가 보이는지?” 등을 매번 눈으로 확인해야 할 때가 많습니다. 이런 반복 작업을 자동으로 검증해주는 도구가 바로 E2E 테스트 도구인데요, 오늘 소개할 Playwright는 그중에서도 강력하고, 사용법도 꽤 직관적인 프레임워크입니다.

Cypress와 같은 기존 E2E 도구와의 가장 큰 차이는, Playwright는 기다림(wait)을 자동으로 처리해준다는 점입니다.
테스트가 깨지는 원인의 대부분은 “페이지가 아직 로드되지 않아서”, “요소가 아직 나타나지 않아서” 같은 문제거든요.
Playwright는 이런 부분을 알아서 감지하고 처리해주기 때문에, 개발자는 기능에 집중한 테스트 코드만 작성하면 됩니다.


Playwright 테스트는 이렇게 구성됩니다

Playwright의 테스트는 크게 두 가지 흐름으로 구성됩니다:

  1. 동작 수행 (action): 페이지에 들어가서 버튼을 클릭하거나 텍스트를 입력하는 것
  2. 상태 검증 (assertion): 화면에 특정 요소가 보이는지, 텍스트가 맞는지 등을 확인하는 것

중요한 건, 이 사이에 기다림(wait)을 명시적으로 넣을 필요가 없다는 점입니다.
Playwright는 버튼을 클릭하거나, 텍스트를 입력하기 전에, 그 요소가 실제로 조작 가능한 상태인지 자동으로 확인합니다.
이를 actionability check라고 부릅니다.

 

또한 Playwright의 assertion(기대값 검증)은 **“결국 이 조건이 만족될 때까지 기다려준다”**는 특징이 있어,
타이밍 이슈나 **경쟁 조건(race condition)**을 피할 수 있습니다.

🧠 경쟁 조건이란?
두 개 이상의 작업이 동시에 실행되며, 실행 순서에 따라 결과가 달라지는 문제 상황을 말합니다.
예를 들어 페이지 이동과 요소 확인이 동시에 일어나면, 요소가 로딩되기 전에 확인하려 해서 테스트가 깨질 수 있어요.

이런 구조 덕분에 Playwright는 flaky(불안정한) 테스트가 줄어들고, 유지보수가 쉬워집니다.


Playwright에서 배우게 될 것들

이 문서에서는 다음과 같은 내용을 다룹니다:

  • 첫 테스트 작성 방법
  • 액션(클릭, 입력 등) 수행하기
  • 기대값(assertion) 확인하기
  • 테스트 간의 격리 처리
  • 테스트 훅(Hook) 사용하기

첫 번째 테스트 작성해보기

테스트는 @playwright/test에서 제공하는 test와 expect를 이용해 작성합니다.

import { test, expect } from '@playwright/test';

test('타이틀에 Playwright 포함 여부 확인', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  await expect(page).toHaveTitle(/Playwright/); // 타이틀에 'Playwright'가 포함되어야 함
});
  • page.goto()로 페이지를 열고,
  • expect(...).toHaveTitle(...)로 기대값을 검사합니다.

한 가지 테스트를 더 볼까요?

 
test('Get started 버튼을 누르고 Installation 헤딩이 보이는지 확인', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  await page.getByRole('link', { name: 'Get started' }).click();
  await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});
  • getByRole은 접근성 기반 역할(role)을 활용해 요소를 찾는 방식입니다.
  • Cypress의 cy.get(...)와는 달리, Playwright는 의미 기반의 선택자를 권장합니다.

💡 팁: JavaScript로 테스트를 작성할 경우 파일 상단에 // @ts-check를 넣으면 VS Code에서 타입 검사를 받을 수 있습니다.


동작(Action) 수행하기

페이지 이동

await page.goto('https://playwright.dev/');
 

Playwright는 페이지가 **완전히 로드(load state)**되기 전까지 자동으로 기다립니다.
따라서 Cypress처럼 cy.wait()를 넣어줄 필요가 없습니다.

요소 조작

Playwright는 Locator API를 사용해 요소를 찾습니다.
Locator는 요소를 지속적으로 추적하기 때문에, 동적으로 DOM이 바뀌어도 유연하게 대응할 수 있습니다.

const getStarted = page.getByRole('link', { name: 'Get started' });
await getStarted.click();

 

보통은 한 줄로 줄여서 이렇게도 많이 씁니다:

await page.getByRole('link', { name: 'Get started' }).click();

 

자주 쓰는 기본 동작

 

locator.check() 체크박스 체크하기
locator.uncheck() 체크박스 체크 해제
locator.click() 클릭하기
locator.hover() 마우스 오버
locator.fill() 입력 필드에 텍스트 입력
locator.focus() 포커스 주기
locator.press() 키보드 키 누르기
locator.setInputFiles() 파일 업로드
locator.selectOption() 드롭다운 옵션 선택
 

기대값 검증(Assertion)

Playwright의 expect()는 Cypress의 should(...)와 유사한 역할을 합니다.
하지만 중요한 차이는, 비동기 matcher를 사용할 수 있다는 점입니다.

expect(success).toBeTruthy(); // 일반 조건 검사
await expect(page).toHaveTitle(/Playwright/); // 비동기 검사 → 타이틀이 조건에 맞을 때까지 기다림

자주 쓰는 비동기 Assertion


toBeChecked() 체크박스 체크됨
toBeEnabled() 요소가 활성화됨
toBeVisible() 화면에 보임
toContainText() 텍스트 포함 여부
toHaveAttribute(attr, val) 특정 속성 포함 여부
toHaveCount(n) 요소 개수 검사
toHaveText('exact text') 정확한 텍스트 검사
toHaveValue('input value') input 값 검사
toHaveTitle(/정규식/) 페이지 타이틀 포함 여부
toHaveURL('정확한 URL') 현재 페이지 URL 검사
 

테스트 격리 (Test Isolation)

Playwright의 테스트는 각각 독립된 브라우저 환경에서 실행됩니다.
이 구조를 가능하게 하는 것이 바로 Browser Context입니다.

test('example test', async ({ page }) => {
  // 이 테스트는 고유한 브라우저 컨텍스트에서 실행됨
});

test('another test', async ({ page }) => {
  // 위와 완전히 다른 컨텍스트에서 실행됨
});
 
 

덕분에 테스트 간 쿠키, 캐시, 로그인 정보 등이 서로 영향을 주지 않죠.


Cypress는 기본적으로 하나의 세션에서 여러 테스트를 돌리기 때문에,
이런 격리를 직접 처리하려면 별도 작업이 필요합니다.


테스트 훅(Hooks) 사용하기

Playwright에서도 beforeEach, afterEach, beforeAll, afterAll 등 다양한 테스트 훅을 사용할 수 있습니다.

 
test.describe('navigation group', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('https://playwright.dev/');
  });

  test('메인 페이지 확인', async ({ page }) => {
    await expect(page).toHaveURL('https://playwright.dev/');
  });
});
 

test.describe()로 테스트를 그룹화하고, 공통으로 실행할 로직을 beforeEach() 안에 넣습니다.
Cypress에서도 비슷하게 beforeEach() 훅을 사용하지만, Playwright는 비동기 처리를 자연스럽게 지원합니다.


마무리

Playwright는 단순히 “테스트 도구 하나 더”가 아닙니다.
프론트엔드 자동화 테스트의 복잡함을 줄여주는 구조적인 해결책이라고 볼 수 있어요.

  • 기다림을 자동으로 처리해주고,
  • 요소와의 상호작용이 직관적이며,
  • 경쟁 조건이나 flaky 테스트를 줄여줍니다.

공식 문서

 

https://playwright.dev/docs/writing-tests

 

Writing tests | Playwright

Introduction

playwright.dev