Playwright의 핵심 개념

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

Playwright의 핵심 개념

Browser, Context, Page

Browser 객체의 역할

Playwright의 Browser 객체는 Playwright를 사용하여 웹 브라우저를 프로그래밍적으로 제어할 때 가장 기본적인 역할을 담당하는 객체입니다. 이는 브라우저 인스턴스를 나타내며, 사용자가 원하는 테스트 시나리오나 자동화를 실행하는 데 중요한 역할을 합니다.

주요 역할과 기능

  1. 브라우저 세션 관리
    • Browser 객체는 브라우저의 수명을 관리합니다. 테스트 실행 중 브라우저를 열고 닫는 작업을 제어합니다.
    • Playwright는 실제 브라우저나 헤드리스(headless) 브라우저 모두 지원하므로, 환경에 따라 유연하게 사용할 수 있습니다.
  2. 새 브라우저 컨텍스트 생성
    • browser.newContext() 메서드를 통해 브라우저 컨텍스트를 생성합니다.
    • 각 컨텍스트는 독립적인 브라우징 환경을 제공하며, 쿠키, 캐시, 스토리지 등도 분리됩니다.
    • 이를 통해 테스트 간 간섭을 방지하고 병렬 테스트가 가능해집니다.
  3. 새 페이지 열기
    • browser.newPage() 메서드를 사용하여 브라우저 탭이나 창을 열 수 있습니다.
    • 각 페이지는 독립적인 탭처럼 동작하며, 웹 애플리케이션과 상호작용하거나 테스트를 수행합니다.
  4. 멀티 브라우저 지원
    • Playwright는 Chromium, Firefox, WebKit 브라우저를 지원합니다.
    • browserType.launch()를 통해 각 브라우저에 해당하는 Browser 객체를 생성할 수 있습니다.
    • 이를 통해 크로스 브라우저 테스트를 간편하게 구현할 수 있습니다.
  5. 리소스 관리
    • 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는 이를 효과적으로 지원합니다.

  1. 독립된 상태 관리
    • BrowserContext는 자체 쿠키와 스토리지를 가집니다.
    • 로그인 세션, 사용자 설정 등이 분리되어 동일한 애플리케이션의 서로 다른 상태를 테스트할 수 있습니다.
  2. 병렬 테스트
    • 한 브라우저에서 여러 BrowserContext를 병렬로 실행할 수 있습니다.
    • 별도의 브라우저 인스턴스를 생성하는 것보다 리소스를 절약하면서도 각 테스트는 완전히 독립적으로 실행됩니다.
  3. 다양한 환경 시뮬레이션
    • 여러 컨텍스트를 생성하여 서로 다른 사용자나 디바이스 환경을 테스트할 수 있습니다.
    • 예: 사용자 A가 로그인한 상태와 사용자 B가 로그인하지 않은 상태를 한 번에 테스트.
  4. 테스트 클린업
    • 테스트 완료 후 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()

테스트 격리의 이점

  1. 상태 충돌 방지
    • 한 테스트의 쿠키, 캐시, 로컬 스토리지가 다른 테스트에 영향을 미치지 않습니다.
  2. 리소스 절약
    • 각각 브라우저를 띄우는 대신 하나의 브라우저에서 여러 컨텍스트를 실행함으로써 메모리와 CPU를 절약할 수 있습니다.
  3. 효율적인 병렬 실행
    • 브라우저 컨텍스트 간에는 격리가 보장되므로 병렬 실행 시에도 테스트의 안정성을 유지할 수 있습니다.

BrowserContext의 주요 메서드

  • browser.new_context(): 새로운 컨텍스트 생성.
  • context.new_page(): 해당 컨텍스트 내에 새로운 페이지(탭) 생성.
  • context.cookies(): 해당 컨텍스트의 쿠키 정보 가져오기.
  • context.clear_cookies(): 컨텍스트의 쿠키 제거.
  • context.close(): 컨텍스트 종료 및 리소스 정리.

BrowserContext는 테스트 간 상태 격리를 보장하면서도 브라우저 리소스를 효율적으로 사용하는 Playwright의 중요한 기능입니다. 이를 통해 병렬 테스트를 간단히 구현할 수 있고, 각 테스트는 독립적으로 수행되므로 충돌이나 간섭 없이 신뢰할 수 있는 결과를 얻을 수 있습니다.

Page 객체와 상호작용

Playwright의 Page 객체는 웹 브라우저의 단일 탭 또는 창을 나타내며, 이를 통해 웹 페이지와 상호작용할 수 있습니다. 테스트 및 자동화 작업에서 Page 객체는 사용자가 브라우저에서 수행하는 다양한 작업(탐색, 클릭, 입력 등)을 프로그래밍적으로 구현하는 데 핵심 역할을 합니다.

Page 객체의 주요 역할

  1. 웹 페이지 탐색
    • 웹 페이지로 이동하거나 리다이렉션을 처리합니다.
    • AJAX 요청이 완료될 때까지 대기할 수 있어 안정적인 테스트를 지원합니다.
  2. DOM 요소 상호작용
    • HTML 요소에 클릭, 입력, 스크롤, 드래그앤드드롭 등의 동작을 수행할 수 있습니다.
    • 요소를 선택하고 데이터 추출(스크레이핑)을 수행할 수도 있습니다.
  3. 상태 대기
    • 특정 이벤트(예: 요소가 나타날 때, 네트워크 요청 완료 등)를 대기하여 테스트의 신뢰성을 높입니다.
  4. 스크린샷 및 PDF 생성
    • 페이지 상태를 캡처하거나 PDF 문서를 생성할 수 있어 테스트 결과를 문서화할 때 유용합니다.
  5. 네트워크 요청 및 응답 제어
    • 네트워크 요청을 가로채고 수정하거나, 모의 응답(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 객체의 이벤트를 통해 특정 시점에 작업을 수행할 수 있습니다.

  1. page.on("load", callback)
    • 페이지가 로드된 후 호출됩니다.
  2. page.on("request", callback)
    • 페이지에서 발생하는 네트워크 요청을 모니터링합니다.
  3. page.on("console", callback)
    • 브라우저 콘솔 출력(로그, 오류 등)을 캡처합니다.
  4. page.on("dialog", callback)
    • alert, confirm, prompt 같은 다이얼로그를 처리합니다.

Playwright의 Page 객체는 웹 페이지와의 상호작용을 위한 강력한 도구입니다. 탐색, 요소 조작, 네트워크 제어, JavaScript 실행 등 다양한 작업을 지원하며, 이러한 기능을 활용해 테스트 및 자동화를 효율적으로 설계할 수 있습니다. Page 객체는 Playwright의 핵심적인 구성 요소로, 실제 사용자 경험을 시뮬레이션하는 데 필수적입니다.


Auto-waiting과 Element Handle

Playwright에서 Auto-waitingElement Handle은 웹 페이지와의 상호작용 및 테스트 자동화를 보다 신뢰성 있게 수행할 수 있도록 돕는 중요한 개념입니다. 두 개념은 페이지 요소와의 작업을 다룰 때 서로 보완적인 역할을 합니다.

1. Auto-waiting

Auto-waiting은 Playwright가 특정 작업(예: 클릭, 입력 등)을 수행하기 전에 관련 조건이 충족될 때까지 자동으로 대기하는 기능입니다. 이를 통해 사용자가 명시적으로 대기 로직을 작성하지 않아도 Playwright가 내부적으로 적절한 타이밍을 기다려 작업을 수행합니다.

Auto-waiting의 작동 방식

  • Playwright는 대상 요소가 적절한 상태인지 확인할 때까지 작업을 지연합니다.
  • 다음과 같은 조건을 만족할 때까지 기다립니다.
    1. 요소가 DOM에 존재해야 함.
    2. 요소가 보여야 하며(visible), 숨겨져 있지 않아야 함.
    3. 요소가 상호작용 가능(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의 장점

  1. 안정성: 페이지 로딩 지연이나 요소 렌더링 문제가 있는 경우에도 동작의 신뢰성을 보장합니다.
  2. 코드 간소화: 명시적인 대기 조건을 추가하지 않아도 되므로 코드가 간결해집니다.
  3. 자동 조건 처리: 숨겨진 요소, 비활성 요소 등을 자동으로 무시하고, 작업이 가능한 상태가 될 때까지 기다립니다.

주의사항

  • 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()
  1. Auto-waiting은 Playwright가 요소의 상태가 적절할 때까지 자동으로 대기하여 안정성과 신뢰성을 높이는 기능입니다. 이를 통해 복잡한 대기 조건을 명시적으로 작성할 필요 없이 효율적인 코드 작성을 지원합니다.
  2. Element Handle은 특정 DOM 요소를 참조하고 세부적으로 작업할 수 있는 도구로, 반복적인 요소 조작이나 고급 제어가 필요한 경우 유용합니다.
  3. 이 두 기능은 함께 사용되며, 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 테스트 구조를 사용하면 다양한 테스트 시나리오를 작성할 수 있습니다.

  1. 페이지 탐색 및 상호작용
    • 페이지 이동, 버튼 클릭, 텍스트 입력, URL 검증 등.
  2. 폼 제출 테스트
    • 로그인 폼, 회원가입 폼 등.
  3. UI 요소 검증
    • 특정 버튼, 텍스트, 이미지 등이 표시되는지 확인.
  4. 다중 브라우저 테스트
    • Chromium, Firefox, WebKit 환경에서의 테스트 병렬 실행.
  5. 네트워크 모니터링
    • API 요청 가로채기, Mock 데이터 제공.

Playwright의 테스트 구조는 다음과 같은 특징을 가집니다.

  • test() 함수를 중심으로 테스트 케이스를 작성.
  • Hooks와 Fixtures를 통해 공통 작업을 관리.
  • Config 파일로 브라우저, 환경 설정을 간단히 구성.
  • Playwright Test 프레임워크를 통해 효율적이고 직관적인 테스트 실행 및 관리가 가능.

Playwright는 이처럼 체계적이고 확장 가능한 테스트 구조를 제공하여 웹 애플리케이션의 테스트 자동화를 간단히 구현할 수 있게 합니다.


Playwright에서 fixtures 디렉토리는 테스트에서 사용되는 공통 상태나 재사용 가능한 설정(예: 브라우저, 로그인 상태, 데이터 설정 등)을 정의하는 데 사용됩니다. 파일명에 대한 규칙은 Playwright가 강제하지 않지만, 일반적으로 가독성명확한 역할 정의를 위해 관습적으로 다음과 같은 명명 규칙을 따릅니다.

1. 일반적인 파일명 규칙

fixtures 디렉토리 내 파일들은 일반적으로 해당 파일이 제공하는 기능에 따라 명명됩니다. 다음은 주요 관례입니다.

  1. 기능 기반 파일명
    • 파일 이름은 해당 fixture가 제공하는 기능이나 역할을 반영해야 합니다.
    • 예:
      • browser.fixtures.ts → 브라우저 관련 설정을 정의.
      • auth.fixtures.ts → 사용자 인증 상태를 설정.
      • data.fixtures.ts → 테스트 데이터 설정 및 초기화.
  2. 범위나 역할에 따른 분류
    • 각 파일은 테스트의 특정 범위나 역할에 따라 이름을 붙입니다.
    • 예:
      • global.fixtures.ts → 테스트 전체에서 사용할 전역 fixtures 정의.
      • ui.fixtures.ts → UI 테스트와 관련된 상태 정의.
      • api.fixtures.ts → API 테스트에서 사용할 공통 상태 정의.
  3. 테스트 유형에 따른 분류
    • 파일 이름에 테스트 대상(브라우저, 페이지, 컨텍스트 등)을 명시하여 명확히 구분합니다.
    • 예:
      • page.fixtures.tsPage 객체와 관련된 설정.
      • 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. 파일명 명명 시 고려할 사항

  1. 일관성
    • 팀에서 사용하는 명명 규칙을 정하고 이를 모든 fixtures 파일에 일관되게 적용합니다.
    • 예: *.fixtures.ts 형식으로 통일.
  2. 모듈화
    • 각 파일이 특정 역할을 담당하도록 작성하여 테스트 코드의 모듈화를 지원합니다.
    • 예: 인증 관련 작업은 auth.fixtures.ts, 브라우저 설정은 browser.fixtures.ts로 분리.
  3. 확장 가능성
    • fixtures를 기능별로 나누어 관리하면 프로젝트가 확장되어도 유지보수하기 쉽습니다.
  4. 파일명 길이
    • 파일명이 지나치게 길어지지 않도록 간결하게 작성합니다. 필요하다면 디렉토리 구조를 활용해 구체적인 역할을 나타낼 수 있습니다.
    • 예: 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 형식을 따르며, 테스트 목적이나 대상에 따라 적절히 세분화하여 유지보수성을 높입니다. 이를 통해 테스트 구조를 더 체계적으로 관리할 수 있습니다.