
selenium에서는 TestNG를 통해서 Java의 annotation을 이용했다. 이처럼 playwright에서 사용되는 Annotation 기능을 알아보고자 포스팅을 한다.
굳이 따지자면 TestNG와 playwright의 annotation이 개념적으로는 테스트를 제어한다는 점에서 비슷하지만 차이점이 더 크다. playwright의 annotation은 주로 테스트 상태를 표시하는 TestNG의 일부 기능만을 담당한다.
playwright에서는 Hooks이 테스트 실행 전후로 특정작업을 수행하는 함수로 사용할 수 있게 하는 방법이다.
Annotations
우선 play wright에서는 테스트 상태와 조건을 표현하기 위해 아래와 같은 방법을 사용한다.
- test.skip() - 테스트 건너뛰기 : 특정환경에서 테스트 할 필요가 없을 때 실행하지 않는 것
- test.fail() - 실패 예상 테스트 : 실패해야 정상인 케이스
- test.fixme() - 수정 필요 테스트 : 테스트가 너무 느리거나 크래시가 일어날 때 테스트를 실행하지 않음, fail로 표시
- test.slow() - 느린 테스트 : 테스트 타임아웃이 3배로 늘려짐
annotations에 커스텀을 해서 type과 description을 이용해 해당 설명을 추가 가능하다. 커스텀 내용을 추가시에는, 테스트 추적이나 분류할 때 편리하다.
test('example test', async ({ page, browser }) => {
test.info().annotations.push({
type: 'browser version',
description: browser.version(),
});
// ...
});
test문에서 조건을 줄 때 skip 하는 경우, 브라우저가 파이어폭스인 경우에 스킵한다
test('skip this test', async ({ page, browserName }) => {
test.skip(browserName === 'firefox', 'Still working on it');
});
테스트 실패했을 경우 스크린샷이나 스크린 녹화가 필요한 경우 아래 예시문처럼 사용할 수 있다.
아래 예시는 테스트 실패 시에만 스크린샷을 찍고, 첫 재 시작 시에만 비디오녹화와 trace를 기록하는 내용이다
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
// Capture screenshot after each test failure.
screenshot: 'only-on-failure',
// Record trace only when retrying a test for the first time.
trace: 'on-first-retry',
// Record video only when retrying a test for the first time.
video: 'on-first-retry'
},
});
Hooks
hooks는 테스트 실행 전후에 특정 작업을 수행하는 함수들이다.
- beforeEach : 각 테스트 실행 전에 실행, 페이지이동이나 로그인 등 공통 setup을 주로 한다.
- afterEach : 각 테스트 실행 후에 실행, 쿠키/캐시 정리 혹은 로그아웃을 주로 한다.
- beforeAll : 모든 테스트 실행 전에 한 번만 실행, 테스트 데이터를 생성한다.
- afterAll : 모든 테스트 실행 후에 한 번만 실행, 생성된 파일 삭제등을 진행한다.
실행순서로 보자면 beforeAll > beforeEach > test문 > afterEach > afterAll 순서로 작동한다.
import { test, expect } from '@playwright/test';
test.describe('로그인 테스트 그룹', () => {
// 모든 테스트 시작 전 한 번 실행
test.beforeAll(async () => {
console.log('테스트 환경 설정');
});
// 각 테스트 전에 실행
test.beforeEach(async ({ page }) => {
await page.goto('https://example.com/login');
});
// 각 테스트 후에 실행
test.afterEach(async ({ page, context }) => {
await context.clearCookies();
});
// 모든 테스트 후 한 번 실행
test.afterAll(async () => {
console.log('테스트 환경 정리');
});
// 일반 테스트
test('유효한 로그인', async ({ page }) => {
await page.fill('#username', 'user@example.com');
await page.fill('#password', 'password123');
await page.click('#login-button');
await expect(page).toHaveURL(/dashboard/);
});
// 조건부 건너뛰기
test('소셜 로그인', async ({ page, browserName }) => {
test.skip(browserName === 'firefox', 'Firefox 미지원');
await page.click('#social-login');
});
// 알려진 버그로 실패 예상
test.fail('비밀번호 찾기 버그', async ({ page }) => {
await page.click('#forgot-password');
await expect(page.locator('.error')).not.toBeVisible();
// 현재 버그로 에러가 보임 → 실패 예상
});
// 느린 테스트
test.slow('이메일 인증 플로우', async ({ page }) => {
// 이메일 받는 시간이 오래 걸림
await page.fill('#email', 'test@example.com');
await page.click('#send-verification');
await page.waitForSelector('.verification-sent', { timeout: 60000 });
});
// 수정 필요한 테스트
test.fixme('2FA 인증 - 크래시 발생', async ({ page }) => {
// 현재 크래시 발생으로 실행 안 함
});
});
그리고 추가로 test.describe.configure()을 통해서 병렬/직렬 제어가 가능하다. 디폴트로 playwright는 병렬 실행이다. 하지만 순차적으로 실행할 경우 모드를 'serial'을 사용하면 된다.
const { test, expect } = require('@playwright/test');
// 순차적 실행
test.describe.configure({ mode: 'serial' });
test.describe('순차 실행 그룹', () => {
test('1번 테스트', async ({ page }) => {
// 이게 끝나야 다음 실행
});
test('2번 테스트', async ({ page }) => {
// 1번 끝난 후 실행
});
});
여기서 첫 실패 시 중단하려면 test.describe.serial을 사용하면 된다.
const { test, expect } = require('@playwright/test');
test.describe.serial('로그인 플로우', () => {
test('1단계: 로그인', async ({ page }) => {
// 실패하면 여기서 멈춤
});
test('2단계: 프로필 확인', async ({ page }) => {
// 1단계 성공해야 실행됨
});
test('3단계: 로그아웃', async ({ page }) => {
// 1,2단계 성공해야 실행됨
});
});
configure({mode:'serial'}) 는 실패해도 다음 테스트를 실행하지만 describe.serial()은 실패하면 나머지를 건너뛰는 차이점이 있다.
만약 테스트문을 작성하다가 특정 테스트문만 실행하고 싶다면 test.only()나 test.describe.only()을 사용하면 가능하다. 하지만 주의점으로는 only()가 있다면 CI에서 문제가 발생할 수 있기 때문에 개발 중에만 사용하는 것이 좋다.
const { test, expect } = require('@playwright/test');
test.only('이것만 실행', async ({ page }) => {
// 이 테스트만 실행됨
});
test('건너뜀', async ({ page }) => {
// 실행 안 됨
});
// 그룹 단위로도 가능
test.describe.only('이 그룹만', () => {
test('테스트 1', async ({ page }) => {});
test('테스트 2', async ({ page }) => {});
});
Fixtures
그리고 playwright에 기능 중에 특이점으로는 커스텀 fixtures라는 것이 있다. 이건 셋업을 할 때 필요한 기능인데 테스트에 필요한 환경이나 데이터를 설정해서 재사용 가능한 객체를 의미한다.
playwright가 자동으로 제공하는 fixtures로는 page(브라우저 페이지), context(브라우저 컨텍스트), browser(브라우저 인스턴스), browserName(브라우저 이름), request(API 요청객체)가 있다.
Hooks는 파일 내 제한적으로 재사용한다면, Fixtures는 전체 프로젝트에 재사용되기 때문에 훨씬 유연성이 높고 여러 테스트 파일에서 공통 setup이 필요할 때 사용되는 것이 좋다.
// fixtures/auth.js
const { test as base } = require('@playwright/test');
exports.test = base.extend({
authenticatedPage: async ({ page }, use) => {
// Setup: 로그인 수행
await page.goto('https://example.com/login');
await page.fill('#email', 'test@example.com');
await page.fill('#password', 'password123');
await page.click('button[type="submit"]');
await page.waitForURL('**/dashboard');
// 테스트에 로그인된 페이지 전달
await use(page);
// Teardown: 로그아웃 (선택사항)
await page.click('#logout');
},
});
// tests/dashboard.spec.js
const { test } = require('../fixtures/auth');
const { expect } = require('@playwright/test');
test('대시보드 확인', async ({ authenticatedPage }) => {
await authenticatedPage.goto('/dashboard');
await expect(authenticatedPage.locator('h1')).toHaveText('Dashboard');
});
test('프로필 수정', async ({ authenticatedPage }) => {
await authenticatedPage.goto('/profile');
await authenticatedPage.fill('#name', 'New Name');
await authenticatedPage.click('#save');
});'자동화(Automation) > Playwright' 카테고리의 다른 글
| Playwright_Assertions (0) | 2025.10.31 |
|---|---|
| Playwright 자동화 Introduction (+window 환경에서 playwright 설치) (0) | 2025.09.28 |