Playwright의 핵심 개념
Playwright의 Browser 객체는 브라우저 세션과 컨텍스트를 관리하며, BrowserContext는 독립된 환경을 제공해 테스트 간 간섭을 방지합니다. Page 객체는 DOM 요소 조작, 네트워크 제어 등 다양한 작업을 지원하며, 동기와 비동기 API를 통해 유연한 자동화가 가능합니다. 테스트는 test(), Fixtures, Hooks로 구성되며, Config 파일로 브라우저 설정과 크로스 브라우저 테스트를 간편하게 관리할 수 있습니다.

Browser, Context, Page
Browser 객체의 역할
Playwright의 Browser
객체는 Playwright를 사용하여 웹 브라우저를 프로그래밍적으로 제어할 때 가장 기본적인 역할을 담당하는 객체입니다. 이는 브라우저 인스턴스를 나타내며, 사용자가 원하는 테스트 시나리오나 자동화를 실행하는 데 중요한 역할을 합니다.
주요 역할과 기능
- 브라우저 세션 관리
Browser
객체는 브라우저의 수명을 관리합니다. 테스트 실행 중 브라우저를 열고 닫는 작업을 제어합니다.- Playwright는 실제 브라우저나 헤드리스(headless) 브라우저 모두 지원하므로, 환경에 따라 유연하게 사용할 수 있습니다.
- 새 브라우저 컨텍스트 생성
browser.newContext()
메서드를 통해 브라우저 컨텍스트를 생성합니다.- 각 컨텍스트는 독립적인 브라우징 환경을 제공하며, 쿠키, 캐시, 스토리지 등도 분리됩니다.
- 이를 통해 테스트 간 간섭을 방지하고 병렬 테스트가 가능해집니다.
- 새 페이지 열기
browser.newPage()
메서드를 사용하여 브라우저 탭이나 창을 열 수 있습니다.- 각 페이지는 독립적인 탭처럼 동작하며, 웹 애플리케이션과 상호작용하거나 테스트를 수행합니다.
- 멀티 브라우저 지원
- Playwright는 Chromium, Firefox, WebKit 브라우저를 지원합니다.
browserType.launch()
를 통해 각 브라우저에 해당하는Browser
객체를 생성할 수 있습니다.- 이를 통해 크로스 브라우저 테스트를 간편하게 구현할 수 있습니다.
- 리소스 관리
browser.close()
를 호출하여 브라우저를 닫고, 리소스를 해제합니다. 이를 통해 메모리 누수를 방지하고 테스트 환경을 정리할 수 있습니다.
예제 코드
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# Chromium 브라우저 실행
browser = p.chromium.launch(headless=False) # headless=False로 실제 브라우저를 띄움
# 새로운 브라우저 컨텍스트 생성
context = browser.new_context()
# 새로운 페이지 열기
page = context.new_page()
page.goto("https://example.com") # URL 접속
print(page.title()) # 페이지 제목 출력
# 브라우저 닫기
browser.close()
Playwright의 Browser
객체는 브라우저를 제어하고 여러 테스트 세션을 설정하는 데 핵심적인 역할을 합니다. 이를 통해 브라우저 환경을 관리하고 테스트 자동화를 간소화하며, 브라우저 간의 일관성 있는 테스트 실행을 보장합니다.
BrowserContext와 테스트 격리
Playwright의 BrowserContext
는 브라우저 내에서 독립된 브라우징 환경을 제공하여 테스트 간의 격리를 보장하는 데 핵심적인 역할을 합니다. BrowserContext
를 활용하면 하나의 브라우저 인스턴스 안에서 독립적으로 동작하는 여러 환경을 생성할 수 있어 효율적이고 안전한 테스트 설계가 가능합니다.
BrowserContext란?
BrowserContext
는 브라우저의 탭이나 세션을 관리하는 컨테이너입니다.- 쿠키, 로컬 스토리지, 세션 스토리지, 캐시 등을 독립적으로 관리하여 테스트 간 간섭을 방지합니다.
- 동일한 브라우저 인스턴스 내에서 여러 컨텍스트를 생성함으로써 병렬 실행과 리소스 효율성을 극대화할 수 있습니다.
테스트 격리와 BrowserContext
테스트 격리는 여러 테스트가 서로 간섭하지 않고 독립적으로 실행되도록 보장하는 것을 의미합니다. Playwright의 BrowserContext
는 이를 효과적으로 지원합니다.
- 독립된 상태 관리
- 각
BrowserContext
는 자체 쿠키와 스토리지를 가집니다. - 로그인 세션, 사용자 설정 등이 분리되어 동일한 애플리케이션의 서로 다른 상태를 테스트할 수 있습니다.
- 각
- 병렬 테스트
- 한 브라우저에서 여러
BrowserContext
를 병렬로 실행할 수 있습니다. - 별도의 브라우저 인스턴스를 생성하는 것보다 리소스를 절약하면서도 각 테스트는 완전히 독립적으로 실행됩니다.
- 한 브라우저에서 여러
- 다양한 환경 시뮬레이션
- 여러 컨텍스트를 생성하여 서로 다른 사용자나 디바이스 환경을 테스트할 수 있습니다.
- 예: 사용자 A가 로그인한 상태와 사용자 B가 로그인하지 않은 상태를 한 번에 테스트.
- 테스트 클린업
- 테스트 완료 후
BrowserContext.close()
를 호출하면 해당 컨텍스트의 모든 상태가 삭제됩니다. - 이는 테스트 후에 리소스를 정리하고, 메모리 누수를 방지하는 데 유용합니다.
- 테스트 완료 후
BrowserContext와 Page의 관계
BrowserContext
는 브라우저 탭(Page)을 생성하는 역할을 합니다.context.new_page()
를 호출하여 특정 컨텍스트 내에 새로운 탭을 열 수 있습니다.- 하나의 컨텍스트 내에서는 동일한 쿠키와 스토리지를 공유하지만, 서로 다른 컨텍스트 간에는 완전히 독립적입니다.
예제 코드: 테스트 격리 활용
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# 브라우저 실행
browser = p.chromium.launch(headless=True)
# 첫 번째 컨텍스트 생성 (사용자 A)
user_a_context = browser.new_context()
user_a_page = user_a_context.new_page()
user_a_page.goto("https://example.com/login")
user_a_page.fill("input#username", "userA")
user_a_page.fill("input#password", "passwordA")
user_a_page.click("button#login")
print("User A logged in:", user_a_page.url)
# 두 번째 컨텍스트 생성 (사용자 B)
user_b_context = browser.new_context()
user_b_page = user_b_context.new_page()
user_b_page.goto("https://example.com/login")
user_b_page.fill("input#username", "userB")
user_b_page.fill("input#password", "passwordB")
user_b_page.click("button#login")
print("User B logged in:", user_b_page.url)
# 두 사용자 간 상태 격리 확인
assert user_a_page.url != user_b_page.url
# 컨텍스트 닫기
user_a_context.close()
user_b_context.close()
browser.close()
테스트 격리의 이점
- 상태 충돌 방지
- 한 테스트의 쿠키, 캐시, 로컬 스토리지가 다른 테스트에 영향을 미치지 않습니다.
- 리소스 절약
- 각각 브라우저를 띄우는 대신 하나의 브라우저에서 여러 컨텍스트를 실행함으로써 메모리와 CPU를 절약할 수 있습니다.
- 효율적인 병렬 실행
- 브라우저 컨텍스트 간에는 격리가 보장되므로 병렬 실행 시에도 테스트의 안정성을 유지할 수 있습니다.
BrowserContext의 주요 메서드
browser.new_context()
: 새로운 컨텍스트 생성.context.new_page()
: 해당 컨텍스트 내에 새로운 페이지(탭) 생성.context.cookies()
: 해당 컨텍스트의 쿠키 정보 가져오기.context.clear_cookies()
: 컨텍스트의 쿠키 제거.context.close()
: 컨텍스트 종료 및 리소스 정리.
BrowserContext
는 테스트 간 상태 격리를 보장하면서도 브라우저 리소스를 효율적으로 사용하는 Playwright의 중요한 기능입니다. 이를 통해 병렬 테스트를 간단히 구현할 수 있고, 각 테스트는 독립적으로 수행되므로 충돌이나 간섭 없이 신뢰할 수 있는 결과를 얻을 수 있습니다.
Page 객체와 상호작용
Playwright의 Page
객체는 웹 브라우저의 단일 탭 또는 창을 나타내며, 이를 통해 웹 페이지와 상호작용할 수 있습니다. 테스트 및 자동화 작업에서 Page
객체는 사용자가 브라우저에서 수행하는 다양한 작업(탐색, 클릭, 입력 등)을 프로그래밍적으로 구현하는 데 핵심 역할을 합니다.
Page
객체의 주요 역할
- 웹 페이지 탐색
- 웹 페이지로 이동하거나 리다이렉션을 처리합니다.
- AJAX 요청이 완료될 때까지 대기할 수 있어 안정적인 테스트를 지원합니다.
- DOM 요소 상호작용
- HTML 요소에 클릭, 입력, 스크롤, 드래그앤드드롭 등의 동작을 수행할 수 있습니다.
- 요소를 선택하고 데이터 추출(스크레이핑)을 수행할 수도 있습니다.
- 상태 대기
- 특정 이벤트(예: 요소가 나타날 때, 네트워크 요청 완료 등)를 대기하여 테스트의 신뢰성을 높입니다.
- 스크린샷 및 PDF 생성
- 페이지 상태를 캡처하거나 PDF 문서를 생성할 수 있어 테스트 결과를 문서화할 때 유용합니다.
- 네트워크 요청 및 응답 제어
- 네트워크 요청을 가로채고 수정하거나, 모의 응답(mock response)을 제공하여 다양한 테스트 시나리오를 구현할 수 있습니다.
주요 메서드와 상호작용 방법
1. 페이지 탐색
page.goto("https://example.com") # 페이지로 이동
page.goto()
는 주어진 URL로 이동합니다.timeout
이나waitUntil
옵션을 지정하여 탐색이 완료될 조건을 설정할 수 있습니다.
2. 요소 찾기 및 상호작용
Playwright는 CSS 선택자, XPath, 텍스트 등 다양한 선택자를 지원합니다.
# 버튼 클릭
page.click("button#submit")
# 텍스트 입력
page.fill("input#username", "my_username")
# 체크박스 선택
page.check("input#agree")
# 드롭다운 선택
page.select_option("select#options", "value1")
# 요소 텍스트 가져오기
text = page.text_content("h1.title")
print(text)
3. 페이지 상태 대기
Playwright는 특정 조건이 만족될 때까지 대기하는 메서드를 제공합니다.
# 특정 요소가 나타날 때까지 대기
page.wait_for_selector("div#success-message")
# 특정 URL로 이동할 때까지 대기
page.wait_for_url("https://example.com/dashboard")
4. 스크린샷 및 PDF 생성
# 스크린샷 저장
page.screenshot(path="screenshot.png")
# PDF 생성
page.pdf(path="output.pdf", format="A4")
5. 네트워크 요청/응답 처리
# 네트워크 요청 가로채기
page.route("https://example.com/api/*", lambda route: route.abort())
# 특정 요청 모의 응답 제공
page.route("https://example.com/api/data", lambda route: route.fulfill(
status=200,
content_type="application/json",
body='{"key":"value"}'
))
6. JavaScript 실행
# 페이지 내에서 JavaScript 실행
result = page.evaluate("() => document.title")
print(result) # 페이지 제목 출력
예제: 간단한 로그인 시나리오
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# 브라우저 및 페이지 설정
browser = p.chromium.launch(headless=False)
context = browser.new_context()
page = context.new_page()
# 페이지 탐색
page.goto("https://example.com/login")
# 로그인 양식 채우기
page.fill("input#username", "test_user")
page.fill("input#password", "test_password")
page.click("button#login")
# 성공 메시지가 나타날 때까지 대기
page.wait_for_selector("div#success-message")
# 성공 메시지 출력
success_message = page.text_content("div#success-message")
print("Login Successful:", success_message)
# 리소스 정리
browser.close()
Page
객체의 주요 이벤트
Playwright는 Page
객체의 이벤트를 통해 특정 시점에 작업을 수행할 수 있습니다.
page.on("load", callback)
- 페이지가 로드된 후 호출됩니다.
page.on("request", callback)
- 페이지에서 발생하는 네트워크 요청을 모니터링합니다.
page.on("console", callback)
- 브라우저 콘솔 출력(로그, 오류 등)을 캡처합니다.
page.on("dialog", callback)
alert
,confirm
,prompt
같은 다이얼로그를 처리합니다.
Playwright의 Page
객체는 웹 페이지와의 상호작용을 위한 강력한 도구입니다. 탐색, 요소 조작, 네트워크 제어, JavaScript 실행 등 다양한 작업을 지원하며, 이러한 기능을 활용해 테스트 및 자동화를 효율적으로 설계할 수 있습니다. Page
객체는 Playwright의 핵심적인 구성 요소로, 실제 사용자 경험을 시뮬레이션하는 데 필수적입니다.
Auto-waiting과 Element Handle
Playwright에서 Auto-waiting과 Element Handle은 웹 페이지와의 상호작용 및 테스트 자동화를 보다 신뢰성 있게 수행할 수 있도록 돕는 중요한 개념입니다. 두 개념은 페이지 요소와의 작업을 다룰 때 서로 보완적인 역할을 합니다.
1. Auto-waiting
Auto-waiting은 Playwright가 특정 작업(예: 클릭, 입력 등)을 수행하기 전에 관련 조건이 충족될 때까지 자동으로 대기하는 기능입니다. 이를 통해 사용자가 명시적으로 대기 로직을 작성하지 않아도 Playwright가 내부적으로 적절한 타이밍을 기다려 작업을 수행합니다.
Auto-waiting의 작동 방식
- Playwright는 대상 요소가 적절한 상태인지 확인할 때까지 작업을 지연합니다.
- 다음과 같은 조건을 만족할 때까지 기다립니다.
- 요소가 DOM에 존재해야 함.
- 요소가 보여야 하며(visible), 숨겨져 있지 않아야 함.
- 요소가 상호작용 가능(interactable)해야 함.
Auto-waiting을 지원하는 메서드
page.click()
: 버튼이 나타나고 클릭 가능한 상태가 될 때까지 기다립니다.page.fill()
: 입력 필드가 활성화될 때까지 기다린 후 텍스트를 입력합니다.page.wait_for_selector()
: 특정 선택자가 DOM에 나타날 때까지 기다립니다.
예제: Auto-waiting 사용
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
# 페이지 로드 및 요소 자동 대기
page.goto("https://example.com")
page.click("button#submit") # 버튼이 활성화될 때까지 자동 대기
page.fill("input#username", "test_user") # 입력 필드가 준비될 때까지 자동 대기
browser.close()
Auto-waiting의 장점
- 안정성: 페이지 로딩 지연이나 요소 렌더링 문제가 있는 경우에도 동작의 신뢰성을 보장합니다.
- 코드 간소화: 명시적인 대기 조건을 추가하지 않아도 되므로 코드가 간결해집니다.
- 자동 조건 처리: 숨겨진 요소, 비활성 요소 등을 자동으로 무시하고, 작업이 가능한 상태가 될 때까지 기다립니다.
주의사항
- Auto-waiting은 Playwright가 기본적으로 처리하지만, 복잡한 시나리오에서는 명시적인 대기(예:
page.wait_for_selector
)가 필요한 경우도 있습니다.
2. Element Handle
Element Handle은 DOM 요소를 직접 참조할 수 있는 객체로, 페이지의 특정 HTML 요소에 대해 더 세부적인 제어와 작업을 수행할 수 있도록 합니다. Page
객체가 요소를 다루는 고수준 메서드를 제공한다면, ElementHandle
은 저수준 제어를 제공합니다.
Element Handle의 생성
page.query_selector()
또는page.wait_for_selector()
를 호출하면 해당 요소의ElementHandle
객체를 가져올 수 있습니다.
element = page.query_selector("div#example") # ElementHandle 객체 생성
Element Handle로 작업
- 생성된
ElementHandle
을 사용하면 특정 요소에 대해 더 세부적인 작업을 수행할 수 있습니다.
# 특정 요소의 텍스트 가져오기
text = element.text_content()
# 특정 요소에 값 입력
element.fill("new value")
# 특정 요소 클릭
element.click()
# 스크린샷 촬영
element.screenshot(path="element.png")
예제: Element Handle 사용
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
# ElementHandle 생성
button = page.query_selector("button#submit")
# ElementHandle로 작업
if button:
button.click() # 버튼 클릭
else:
print("Button not found!")
browser.close()
Element Handle vs Page 객체
기능 | Page 객체 | Element Handle |
---|---|---|
사용 범위 | 페이지 전체의 요소를 다룸 | 특정 요소를 참조하고 세부적으로 다룸 |
대상 | CSS 선택자, XPath 등을 통해 요소를 찾음 | 찾은 요소를 핸들로 유지하며 조작 |
상호작용 수준 | 고수준(자동화 위주) | 저수준(세부 제어 및 커스터마이징 가능) |
일회성 작업 | 특정 작업 후 요소 참조를 버림 | ElementHandle을 통해 요소를 반복 참조 |
Auto-waiting과 Element Handle의 조합
두 기능은 서로 보완적으로 사용할 수 있습니다. Auto-waiting은 페이지 상태를 안정적으로 유지하며 작업을 진행하고, Element Handle은 특정 요소를 지속적으로 조작할 때 유용합니다.
예제: Auto-waiting과 Element Handle 결합
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
# 요소가 나타날 때까지 대기 (Auto-waiting)
element = page.wait_for_selector("input#username")
# Element Handle로 세부 작업
if element:
element.fill("test_user") # 사용자 이름 입력
else:
print("Element not found!")
browser.close()
- Auto-waiting은 Playwright가 요소의 상태가 적절할 때까지 자동으로 대기하여 안정성과 신뢰성을 높이는 기능입니다. 이를 통해 복잡한 대기 조건을 명시적으로 작성할 필요 없이 효율적인 코드 작성을 지원합니다.
- Element Handle은 특정 DOM 요소를 참조하고 세부적으로 작업할 수 있는 도구로, 반복적인 요소 조작이나 고급 제어가 필요한 경우 유용합니다.
- 이 두 기능은 함께 사용되며, Auto-waiting이 요소의 준비 상태를 보장하고, Element Handle은 세부적인 제어를 제공합니다.
동기와 비동기 처리
동기(synchronous)와 비동기(asynchronous) 처리는 프로그램에서 작업을 수행하는 방식에 대한 두 가지 근본적인 개념으로, 각각 작업의 흐름과 실행 순서에 큰 차이를 가집니다. 이들은 특히 Playwright 같은 비동기 프레임워크에서 중요한 역할을 합니다.
1. 동기 처리 (Synchronous)
특징
- 작업 순차 처리: 하나의 작업이 완료될 때까지 다음 작업이 대기합니다.
- 단일 작업 흐름: 현재 작업이 완료되기 전에는 다른 작업을 진행하지 않습니다.
- 코드 가독성 높음: 작업 순서가 직관적이고 읽기 쉽습니다.
장점
- 코드의 실행 순서가 논리적이며 이해하기 쉽습니다.
- 디버깅이 간단하고 예측 가능성이 높습니다.
단점
- 하나의 작업이 오래 걸리면 전체 프로그램이 멈춥니다.
- 효율성이 낮아질 수 있습니다(특히 I/O 작업에서).
예제: 동기 처리
def process_task():
print("Task 1 시작")
print("Task 1 완료")
print("Task 2 시작")
print("Task 2 완료")
process_task()
출력:
Task 1 시작
Task 1 완료
Task 2 시작
Task 2 완료
2. 비동기 처리 (Asynchronous)
특징
- 작업 병렬 처리: 작업이 완료되기를 기다리지 않고 다음 작업을 바로 시작합니다.
- 이벤트 루프 기반: 비동기 작업은 이벤트 루프에 의해 관리되고, 작업이 완료되면 그 결과를 처리합니다.
- 작업 간 독립성: 하나의 작업이 다른 작업에 영향을 주지 않습니다.
장점
- CPU와 I/O 리소스를 더 효율적으로 활용할 수 있습니다.
- 대규모 네트워크 요청, 파일 처리 등에서 성능이 뛰어납니다.
단점
- 코드가 복잡해질 수 있으며, 실행 순서를 추적하기 어려울 수 있습니다.
- 디버깅 및 예외 처리가 어려울 수 있습니다.
예제: 비동기 처리
import asyncio
async def task1():
print("Task 1 시작")
await asyncio.sleep(1) # 비동기 대기
print("Task 1 완료")
async def task2():
print("Task 2 시작")
await asyncio.sleep(2) # 비동기 대기
print("Task 2 완료")
async def main():
await asyncio.gather(task1(), task2()) # 병렬 실행
asyncio.run(main())
출력:
Task 1 시작
Task 2 시작
Task 1 완료
Task 2 완료
3. Playwright에서의 동기와 비동기
Playwright는 주로 비동기 API를 사용하며, Python에서는 async
키워드를 통해 비동기 처리가 가능합니다. 하지만 동기 API도 지원하므로 필요에 따라 선택적으로 사용할 수 있습니다.
Playwright 동기 방식
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
browser.close()
Playwright 비동기 방식
from playwright.async_api import async_playwright
async def run():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
page = await browser.new_page()
await page.goto("https://example.com")
print(await page.title())
await browser.close()
import asyncio
asyncio.run(run())
비교: 동기 vs 비동기
특징 | 동기 처리 | 비동기 처리 |
---|---|---|
실행 방식 | 작업이 완료될 때까지 대기 | 작업 완료를 기다리지 않고 다음 작업 수행 |
처리 순서 | 코드 작성 순서대로 실행 | 작업 완료 시 콜백 또는 이벤트로 결과 처리 |
효율성 | 느린 작업에 의해 성능 저하 가능 | 리소스 활용이 효율적 (특히 I/O 작업에서) |
복잡도 | 코드가 단순하고 직관적 | 코드 복잡도 증가 가능 (콜백, await 등) |
사용 사례 | 단일 작업, 순차적 처리 | 네트워크 요청, 파일 처리, 대규모 병렬 작업 |
4. 언제 동기와 비동기를 사용할까?
동기를 사용하는 경우
- 간단한 작업이나 테스트 자동화처럼 작업의 순서가 중요할 때.
- 작업 시간이 짧고, 결과를 기다려야 하는 경우.
비동기를 사용하는 경우
- 파일 입출력, 네트워크 요청처럼 시간이 오래 걸리는 작업이 많을 때.
- 다수의 작업을 동시에 처리하여 성능을 극대화해야 할 때.
- 사용자 인터페이스(UI)에서 긴 작업으로 인해 응답성을 유지해야 할 때.
동기 처리는 작업 순서가 직관적이고 디버깅이 쉬운 반면, 시간이 오래 걸리는 작업에서 성능 저하를 초래할 수 있습니다. 비동기 처리는 자원을 효율적으로 활용하고 성능을 향상시키지만, 코드의 복잡성이 높아질 수 있습니다. Playwright는 동기와 비동기 모두 지원하며, 프로젝트의 요구 사항에 따라 적절한 방식을 선택해 사용할 수 있습니다.
Playwright의 테스트 구조
Playwright의 테스트 구조는 효율적이고 체계적으로 테스트를 작성, 실행, 관리할 수 있도록 설계되었습니다. 특히, 테스트 자동화와 브라우저 기반 애플리케이션의 테스트를 간단히 구현할 수 있는 기능을 제공합니다. 아래는 Playwright 테스트 구조의 주요 구성 요소와 원리에 대한 설명입니다.
Playwright 테스트 구조의 주요 개념
Playwright의 테스트 구조는 크게 다음과 같은 단계 및 구성 요소로 이루어져 있습니다.
1. 테스트 파일
- 테스트를 작성하는 개별 파일.
- 일반적으로
.spec.js
,.spec.ts
,.test.js
,.test.ts
같은 파일 확장자를 사용합니다. - 한 파일 내에서 여러 테스트 케이스를 정의할 수 있습니다.
2. 테스트 라이브러리
- Playwright는 자체적으로 제공하는 Playwright Test 프레임워크를 기본으로 사용합니다.
- Jest와 유사한 방식으로 동작하며, 테스트 환경 설정과 실행에 필요한 기능을 제공합니다.
3. 테스트 함수
- Playwright의 기본 테스트 함수는
test()
로, 각 테스트 케이스를 정의합니다. test()
함수 내부에서 특정 작업을 실행하고, 어설션(assertions)을 사용해 결과를 검증합니다.
4. Before/After Hooks
- 테스트의 실행 전후에 공통 작업을 수행하도록 설정할 수 있는 훅(hooks)을 제공합니다.
- 예: 로그인, 초기화, 클린업 등.
- 주요 메서드:
test.beforeAll()
: 모든 테스트 전에 한 번 실행.test.afterAll()
: 모든 테스트 후에 한 번 실행.test.beforeEach()
: 각 테스트 전에 실행.test.afterEach()
: 각 테스트 후에 실행.
5. Fixtures
- Fixtures는 테스트에 필요한 상태나 객체를 설정하는 데 사용됩니다.
- Playwright는 기본적인 브라우저, 페이지, 컨텍스트를 포함한 여러 기본 fixtures를 제공합니다.
6. Assertions
- Playwright는 결과를 검증하기 위한 다양한 어설션 API를 제공합니다.
- 기본적으로
expect
를 사용하며, 페이지 상태, 요소 상태 등을 검증할 수 있습니다.
Playwright 테스트 구조 예제
기본적인 테스트 구조
import { test, expect } from '@playwright/test';
test('Example test: Verify page title', async ({ page }) => {
// 페이지로 이동
await page.goto('https://example.com');
// 페이지 제목 검증
await expect(page).toHaveTitle('Example Domain');
});
Before/After Hooks 활용
import { test, expect } from '@playwright/test';
test.beforeEach(async ({ page }) => {
// 각 테스트 전에 로그인 페이지로 이동
await page.goto('https://example.com/login');
});
test.afterEach(async ({ page }) => {
// 각 테스트 후에 로그아웃 수행
await page.click('button#logout');
});
test('Test 1: Perform login', async ({ page }) => {
await page.fill('input#username', 'test_user');
await page.fill('input#password', 'test_password');
await page.click('button#submit');
await expect(page).toHaveURL('https://example.com/dashboard');
});
test('Test 2: Verify dashboard', async ({ page }) => {
await expect(page.locator('h1')).toHaveText('Welcome, test_user');
});
Playwright Test의 기본 디렉토리 구조
Playwright 프로젝트는 일반적으로 아래와 같은 디렉토리 구조를 따릅니다.
project/
├── playwright.config.ts # Playwright 설정 파일
├── tests/ # 테스트 파일 디렉토리
│ ├── example.spec.ts # 테스트 파일 (여러 개 가능)
│ ├── login.spec.ts # 로그인 관련 테스트
│ └── dashboard.spec.ts # 대시보드 관련 테스트
├── fixtures/ # Custom fixture 파일
├── reports/ # 테스트 결과 보고서
└── package.json # Node.js 설정 파일
Playwright Config 파일
Playwright는 playwright.config.ts
파일에서 설정을 관리합니다. 이 파일을 통해 브라우저 옵션, 테스트 디렉토리, 실행 환경 등을 정의할 수 있습니다.
예제 Config 파일
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests', // 테스트 파일 위치
timeout: 30000, // 테스트 타임아웃 (ms)
retries: 2, // 실패한 테스트 재시도 횟수
use: {
headless: true, // 브라우저 헤드리스 모드 설정
baseURL: 'https://example.com', // 기본 URL
screenshot: 'on', // 테스트 중 스크린샷 저장
video: 'retain-on-failure', // 실패 시 비디오 기록
},
projects: [
{ name: 'Chromium', use: { browserName: 'chromium' } },
{ name: 'Firefox', use: { browserName: 'firefox' } },
{ name: 'WebKit', use: { browserName: 'webkit' } },
],
});
Playwright 테스트 실행
테스트는 아래 명령어를 통해 실행할 수 있습니다.
보고서 생성
npx playwright show-report
테스트 필터링
npx playwright test --grep "Test 1"
특정 파일의 테스트 실행
npx playwright test tests/example.spec.ts
모든 테스트 실행
npx playwright test
주요 테스트 사례
Playwright 테스트 구조를 사용하면 다양한 테스트 시나리오를 작성할 수 있습니다.
- 페이지 탐색 및 상호작용
- 페이지 이동, 버튼 클릭, 텍스트 입력, URL 검증 등.
- 폼 제출 테스트
- 로그인 폼, 회원가입 폼 등.
- UI 요소 검증
- 특정 버튼, 텍스트, 이미지 등이 표시되는지 확인.
- 다중 브라우저 테스트
- Chromium, Firefox, WebKit 환경에서의 테스트 병렬 실행.
- 네트워크 모니터링
- API 요청 가로채기, Mock 데이터 제공.
Playwright의 테스트 구조는 다음과 같은 특징을 가집니다.
test()
함수를 중심으로 테스트 케이스를 작성.- Hooks와 Fixtures를 통해 공통 작업을 관리.
- Config 파일로 브라우저, 환경 설정을 간단히 구성.
- Playwright Test 프레임워크를 통해 효율적이고 직관적인 테스트 실행 및 관리가 가능.
Playwright는 이처럼 체계적이고 확장 가능한 테스트 구조를 제공하여 웹 애플리케이션의 테스트 자동화를 간단히 구현할 수 있게 합니다.
Playwright에서 fixtures
디렉토리는 테스트에서 사용되는 공통 상태나 재사용 가능한 설정(예: 브라우저, 로그인 상태, 데이터 설정 등)을 정의하는 데 사용됩니다. 파일명에 대한 규칙은 Playwright가 강제하지 않지만, 일반적으로 가독성과 명확한 역할 정의를 위해 관습적으로 다음과 같은 명명 규칙을 따릅니다.
1. 일반적인 파일명 규칙
fixtures
디렉토리 내 파일들은 일반적으로 해당 파일이 제공하는 기능에 따라 명명됩니다. 다음은 주요 관례입니다.
- 기능 기반 파일명
- 파일 이름은 해당
fixture
가 제공하는 기능이나 역할을 반영해야 합니다. - 예:
browser.fixtures.ts
→ 브라우저 관련 설정을 정의.auth.fixtures.ts
→ 사용자 인증 상태를 설정.data.fixtures.ts
→ 테스트 데이터 설정 및 초기화.
- 파일 이름은 해당
- 범위나 역할에 따른 분류
- 각 파일은 테스트의 특정 범위나 역할에 따라 이름을 붙입니다.
- 예:
global.fixtures.ts
→ 테스트 전체에서 사용할 전역fixtures
정의.ui.fixtures.ts
→ UI 테스트와 관련된 상태 정의.api.fixtures.ts
→ API 테스트에서 사용할 공통 상태 정의.
- 테스트 유형에 따른 분류
- 파일 이름에 테스트 대상(브라우저, 페이지, 컨텍스트 등)을 명시하여 명확히 구분합니다.
- 예:
page.fixtures.ts
→Page
객체와 관련된 설정.context.fixtures.ts
→ 브라우저 컨텍스트 관련 설정.
2. 파일명 예시
다음은 fixtures
디렉토리 구조와 파일명 예시입니다.
fixtures/
├── auth.fixtures.ts # 사용자 인증 상태를 설정하는 fixture
├── browser.fixtures.ts # 브라우저 관련 설정
├── context.fixtures.ts # BrowserContext 설정
├── data.fixtures.ts # 테스트 데이터 초기화
├── global.fixtures.ts # 전역 fixture 설정
├── ui.fixtures.ts # UI 테스트 관련 fixture
└── api.fixtures.ts # API 테스트에서 활용할 fixture
3. Playwright 테스트에서 fixtures
활용 규칙
Playwright에서 fixtures
는 자동으로 주입되므로 파일명은 가독성과 유지보수성에 초점을 맞춰야 합니다. 아래는 fixtures
작성과 관련된 팁입니다.
기본적인 fixtures
정의
// fixtures/auth.fixtures.ts
import { test as base, expect } from '@playwright/test';
// Custom fixture 정의
export const test = base.extend<{
login: () => Promise<void>;
}>({
login: async ({ page }, use) => {
// 로그인 구현
await page.goto('https://example.com/login');
await page.fill('input#username', 'test_user');
await page.fill('input#password', 'test_password');
await page.click('button#submit');
await use(); // Custom fixture 주입
},
});
export { expect };
테스트에서 fixtures
사용
// tests/example.spec.ts
import { test, expect } from '../fixtures/auth.fixtures';
test('Verify login flow', async ({ page, login }) => {
await login(); // Fixture 호출
await expect(page).toHaveURL('https://example.com/dashboard');
});
4. 파일명 명명 시 고려할 사항
- 일관성
- 팀에서 사용하는 명명 규칙을 정하고 이를 모든
fixtures
파일에 일관되게 적용합니다. - 예:
*.fixtures.ts
형식으로 통일.
- 팀에서 사용하는 명명 규칙을 정하고 이를 모든
- 모듈화
- 각 파일이 특정 역할을 담당하도록 작성하여 테스트 코드의 모듈화를 지원합니다.
- 예: 인증 관련 작업은
auth.fixtures.ts
, 브라우저 설정은browser.fixtures.ts
로 분리.
- 확장 가능성
fixtures
를 기능별로 나누어 관리하면 프로젝트가 확장되어도 유지보수하기 쉽습니다.
- 파일명 길이
- 파일명이 지나치게 길어지지 않도록 간결하게 작성합니다. 필요하다면 디렉토리 구조를 활용해 구체적인 역할을 나타낼 수 있습니다.
- 예:
auth/login.fixtures.ts
(로그인 관련 설정만).
5. 추천 파일명 규칙
- 형식:
<기능명>.fixtures.ts
- 조합:
<범위>.<기능>.fixtures.ts
(필요 시 더 세부적으로 구분) - 관례적 예시:
auth.fixtures.ts
: 인증 관련 설정.browser.fixtures.ts
: 브라우저 설정.data.fixtures.ts
: 데이터 초기화.global.fixtures.ts
: 전역 상태 설정.ui.fixtures.ts
: UI 테스트 관련.
Playwright에서 fixtures
파일의 이름은 기능과 역할을 직관적으로 나타내는 것이 중요합니다. 파일명은 보통 *.fixtures.ts
형식을 따르며, 테스트 목적이나 대상에 따라 적절히 세분화하여 유지보수성을 높입니다. 이를 통해 테스트 구조를 더 체계적으로 관리할 수 있습니다.