GitHub Actions × Playwright로 E2E 테스트를 자동화하는 방법|CI/CD 설정을 처음부터 해설

테스트 자동화

GitHub Actions와 Playwright를 조합하면, 코드를 푸시할 때마다 E2E 테스트가 자동 실행되는 CI/CD 파이프라인을 추가 비용 없이 구축할 수 있습니다.

📌 이런 분께 추천합니다

  • Playwright의 E2E 테스트를 CI/CD에 통합하고 싶은 QA 엔지니어·개발자
  • GitHub Actions를 처음 사용하며 설정 파일 작성법을 처음부터 배우고 싶은 분
  • 푸시할 때마다 자동으로 테스트가 실행되는 환경을 간단하게 만들고 싶은 분
  • 테스트 결과 리포트를 GitHub 상에서 확인하고 싶은 분

✅ 이 글을 읽으면 얻을 수 있는 것

  • GitHub Actions의 YAML 설정 파일을 처음부터 작성할 수 있게 된다
  • Playwright × pytest 테스트를 CI 환경에서 자동 실행할 수 있다
  • HTML 리포트를 GitHub Actions의 아티팩트로 저장·확인할 수 있다

👤 이 글을 쓴 사람

QA 엔지니어로서 실무에서 Selenium·Playwright·pytest를 활용한 테스트 자동화에 종사 중입니다. GitHub Actions 통합도 실제 프로젝트에서 경험했으며, 빠지기 쉬운 포인트를 포함해 현장 시각으로 해설합니다. 구현 코드는 GitHub에 공개 중입니다: github.com/YOSHITSUGU728/automated-testing-portfolio

📌 결론:이 글에서 만들 수 있는 것

  • 자동 테스트 실행:main 브랜치로의 푸시·PR에서 자동으로 E2E 테스트가 실행된다
  • 리포트 저장:테스트 결과 HTML을 아티팩트로 GitHub 상에서 확인할 수 있다
  • 무료로 운용:GitHub Actions는 퍼블릭 리포지토리 무제한·프라이빗도 월 2,000분 무료

로컬에서 테스트가 통과해도 팀원이 푸시한 코드로 인해 깨지는 경우는 흔히 있습니다. CI/CD에 E2E 테스트를 통합하면 「누가 푸시해도 자동으로 테스트가 실행되고, 실패하면 알림이 오는」 구조를 만들 수 있습니다. 이 글에서는 GitHub Actions × Playwright × pytest 구성으로, 실제로 동작하는 CI/CD 파이프라인을 처음부터 구축하는 절차를 해설합니다.

CI/CD와 GitHub Actions의 기본

CI(지속적 통합)란 코드 푸시나 PR을 트리거로 자동으로 빌드·테스트를 실행하는 구조입니다. GitHub Actions는 GitHub에 내장된 CI/CD 서비스로, 리포지토리에 .github/workflows/ 폴더를 만들고 YAML 파일을 두는 것만으로 동작합니다.

항목내용
실행 환경Ubuntu / Windows / macOS 선택 가능
무료 플랜퍼블릭 리포지토리:무제한 프라이빗:월 2,000분
트리거push·pull_request·schedule·수동 실행 등
설정 파일.github/workflows/*.yml
Playwright 대응공식 Docker 이미지 있음·헤드리스 실행 대응

① 프로젝트 구성

이번에 사용할 프로젝트 구성입니다.

my_project/
├── .github/
│   └── workflows/
│       └── playwright.yml    # GitHub Actions 워크플로우 파일
├── tests/
│   ├── conftest.py
│   └── test_saucedemo.py     # Playwright 테스트
├── requirements.txt          # 패키지 의존성 목록
└── pytest.ini                # pytest 설정

② requirements.txt 준비

CI 환경에서 설치할 패키지를 requirements.txt에 정리합니다.

pytest
pytest-playwright
pytest-html
playwright
💡 포인트:버전을 고정하려면 pytest==7.4.0처럼 작성합니다. CI 환경에서도 같은 버전이 사용되므로 로컬과 동작이 일치하기 쉬워집니다. playwrightpytest-playwright가 의존성으로 설치하는 경우도 있지만, 환경에 따라 설치되지 않는 케이스가 있으므로 명시적으로 기재해 두는 것이 안전합니다.

③ 테스트 코드 확인

CI에서 실행할 Playwright 테스트 샘플입니다. SauceDemo의 로그인 플로우를 테스트합니다.

# tests/test_saucedemo.py
from playwright.sync_api import Page

def test_로그인_정상(page: Page):
    page.goto("https://www.saucedemo.com/")
    page.fill("#user-name", "standard_user")
    page.fill("#password", "secret_sauce")
    page.click("#login-button")
    assert page.url.endswith("/inventory.html")

def test_로그인_비밀번호_오류(page: Page):
    page.goto("https://www.saucedemo.com/")
    page.fill("#user-name", "standard_user")
    page.fill("#password", "wrong_password")
    page.click("#login-button")
    error_msg = page.locator(".error-message-container").text_content()
    assert "Epic sadface" in error_msg
# pytest.ini
[pytest]
testpaths = tests
addopts = -v --tb=short --html=reports/report.html --self-contained-html

④ GitHub Actions YAML 파일 작성(핵심)

이것이 이번 설정의 핵심입니다. .github/workflows/playwright.yml을 작성합니다.

# .github/workflows/playwright.yml

name: Playwright E2E Tests

# ① 트리거 설정:main으로의 push·PR에서 자동 실행
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    # ② 실행 환경:Ubuntu 최신판 사용
    runs-on: ubuntu-latest

    steps:
      # ③ 리포지토리 코드를 체크아웃
      - name: 리포지토리 체크아웃
        uses: actions/checkout@v4

      # ④ Python 설정
      - name: Python 3.11 설정
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      # ⑤ 의존 패키지 설치(안정성을 위해 pip를 먼저 업그레이드)
      - name: 의존 패키지 설치
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

      # ⑥ Playwright 브라우저 설치
      - name: Playwright 브라우저 설치
        run: playwright install chromium --with-deps

      # ⑦ reports 폴더 생성(없으면 아티팩트 저장이 실패함)
      - name: reports 폴더 생성
        run: mkdir -p reports

      # ⑧ 테스트 실행
      - name: E2E 테스트 실행
        run: pytest

      # ⑨ 테스트 결과 리포트를 아티팩트로 저장
      - name: 리포트 업로드
        uses: actions/upload-artifact@v4
        if: always()    # 테스트 실패 시에도 반드시 실행
        with:
          name: playwright-report
          path: reports/
          retention-days: 30

각 스텝의 의미를 정리합니다:

스텝무엇을 하는가
① 트리거main으로의 push 또는 PR 생성 시 자동 실행
② runs-onUbuntu 최신판을 실행 환경으로 사용
③ checkoutGitHub의 코드를 CI 환경에 클론
④ setup-pythonPython 3.11을 CI 환경에 설치
⑤ pip installpip를 먼저 업그레이드한 후 requirements.txt의 패키지를 설치
⑥ playwright installChromium 브라우저와 Linux 시스템 의존 라이브러리를 설치
⑦ mkdir -p reports리포트 저장용 폴더를 사전에 생성(없으면 저장 스텝이 실패)
⑧ pytest테스트를 실행(실패 시 잡이 실패 처리됨)
⑨ upload-artifactHTML 리포트를 GitHub 상에 30일간 저장

⑤ 실행 결과 확인 방법

YAML 파일을 GitHub에 푸시하면 워크플로우가 자동으로 시작됩니다. 확인 절차는 다음과 같습니다.

순서조작
① Actions 탭 열기GitHub 리포지토리 상단 메뉴에서 「Actions」를 클릭
② 워크플로우 선택「Playwright E2E Tests」를 선택하여 실행 이력 확인
③ 로그 확인각 스텝을 클릭하면 실행 로그가 표시됨
④ 리포트 다운로드페이지 하단의 「Artifacts」에서 playwright-report를 다운로드

실행 로그 샘플

Run pytest
========================= test session starts ==========================
platform linux -- Python 3.11.0, pytest-7.4.0
collected 2 items

tests/test_saucedemo.py::test_로그인_정상        PASSED  [ 50%]
tests/test_saucedemo.py::test_로그인_비밀번호_오류  PASSED  [100%]

========================== 2 passed in 8.43s ===========================

⑥ 응용:스케줄 실행·복수 브라우저 대응

매일 심야에 자동 실행하기

푸시 시뿐만 아니라 정기적으로 테스트를 실행하고 싶은 경우에는 schedule을 추가합니다.

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  schedule:
    - cron: "0 0 * * *"   # 매일 UTC 0:00(한국 시간 09:00)에 실행

복수 브라우저로 병렬 테스트

strategy: matrix를 사용하면 Chromium·Firefox·WebKit의 3개 브라우저에서 테스트를 병렬 실행할 수 있습니다.

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        browser: [chromium, firefox, webkit]

    steps:
      # ... 이전 스텝과 동일 ...

      - name: Playwright 브라우저 설치
        run: playwright install ${{ matrix.browser }} --with-deps

      - name: E2E 테스트 실행(${{ matrix.browser }})
        run: pytest --browser ${{ matrix.browser }}

      - name: 리포트 업로드
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: report-${{ matrix.browser }}
          path: reports/
💡 실무 팁:3개 브라우저를 병렬로 실행하면 실행 시간은 거의 변하지 않으면서 브라우저 커버리지가 3배가 됩니다. 단, 무료 플랜 소비도 3배가 됩니다. 실무에서는 main 브랜치만 3개 브라우저·PR은 Chromium만이라는 사용 구분이 자주 채택됩니다.

FAQ

Q. GitHub Actions는 정말 무료로 사용할 수 있나요?

퍼블릭 리포지토리는 무제한 무료입니다. 프라이빗 리포지토리는 월 2,000분의 무료 플랜이 있습니다. Playwright × pytest 테스트는 1회당 1〜3분 정도가 많으므로, 개인 프로젝트나 소규모 팀이라면 무료 플랜으로 충분한 경우가 대부분입니다.

Q. 테스트가 실패했을 때 알림을 받을 수 있나요?

네. GitHub 계정 설정에서 「Actions」 알림을 켜면 워크플로우가 실패했을 때 이메일이 옵니다. Slack 알림을 설정하고 싶은 경우에는 slackapi/slack-github-action을 사용하면 YAML 몇 줄로 실현할 수 있습니다.

Q. 로컬에서 통과한 테스트가 CI에서 실패하는 이유는?

가장 많은 원인은 ①환경 차이(Python 버전·OS의 차이), ②헤드리스 모드에서의 동작 차이, ③테스트 대상 서비스로의 네트워크 연결 실패의 3가지입니다. requirements.txt의 버전을 고정하는 것과 스택 트레이스를 꼼꼼히 확인하는 것이 처음 대응 방법입니다.

Q. 테스트 대상이 localhost(로컬 서버)인 경우는 어떻게 하면 되나요?

GitHub Actions의 스텝 내에서 서버를 기동하면 대응할 수 있습니다. 예를 들어 Python의 Flask 앱이라면 run: python app.py &로 백그라운드 기동하고, 그 후 pytest를 실행하는 스텝을 추가합니다. 외부 서비스(DB·API 서버)가 필요한 경우에는 services 키로 Docker 컨테이너를 기동할 수도 있습니다.

⚠️ GitHub Actions × Playwright에서 자주 발생하는 함정 8선

CI 환경 특유의 트러블이 많은 것이 GitHub Actions × Playwright의 조합입니다. 로컬에서 동작했는데 CI에서 실패한다……는 경험을 하지 않으려면 미리 파악해 두세요.

playwright install을 잊어 브라우저를 찾을 수 없다

CI 환경에는 Playwright의 브라우저가 설치되어 있지 않습니다. pip install pytest-playwright으로 Python 패키지를 설치해도 브라우저 본체는 별도로 playwright install chromium --with-deps가 필요합니다. --with-deps를 잊으면 Linux의 의존 라이브러리가 부족하여 브라우저가 기동하지 않습니다.

if: always()를 쓰지 않으면 테스트 실패 시 리포트가 저장되지 않는다

아티팩트 업로드 스텝에 if: always()를 쓰지 않으면, 테스트가 실패한 순간에 잡이 중단되어 리포트가 저장되지 않습니다. 실패 시일수록 리포트가 필요하므로 반드시 써야 합니다.

③ YAML 인덴트 오류로 워크플로우가 시작되지 않는다

GitHub의 YAML은 인덴트에 매우 엄격합니다. 스페이스 2개와 4개의 혼재, 탭 문자의 혼입 등으로 「Invalid workflow file」 에러가 납니다. yamllint.com에서 YAML 문법 체크를 하고 나서 푸시하는 것이 확실합니다.

reports/ 폴더가 없으면 아티팩트 저장이 실패한다

pytest 실행에서 테스트가 전부 스킵된 경우 등 reports/ 폴더가 만들어지지 않는 경우가 있습니다. 그 경우 upload-artifact 스텝도 실패합니다. 회피책으로 업로드 전에 run: mkdir -p reports를 추가하거나, if-no-files-found: warn 옵션을 아티팩트 설정에 추가해 두세요.

⑤ 헤드리스 모드에서 요소를 찾을 수 없다(로컬은 통과하는데 CI에서 실패)

CI 환경은 기본적으로 헤드리스 모드로 실행됩니다. 헤드풀 모드에 비해 렌더링 타이밍에 차이가 있어 page.locator()가 요소를 찾지 못하는 경우가 있습니다. page.wait_for_selector()expect(locator).to_be_visible()을 사용하여 명시적인 대기를 추가하면 해결됩니다.

⑥ matrix 병렬 실행에서 아티팩트 이름이 충돌하여 리포트가 덮어써진다

복수 브라우저의 병렬 테스트에서 모두 같은 name: playwright-report로 하면 나중에 완료된 잡이 이전 리포트를 덮어써 버립니다. name: report-${{ matrix.browser }}처럼 브라우저 이름을 포함하여 유니크하게 만드세요.

actions/checkout@v4 등의 버전을 생략하면 예고 없이 동작이 변한다

uses: actions/checkout@main처럼 최신 브랜치를 지정하면 액션이 업데이트된 타이밍에 갑자기 워크플로우가 깨지는 경우가 있습니다. @v4처럼 메이저 버전을 고정해 두는 것이 안전합니다.

⑧ pip 캐시가 남아 ModuleNotFoundError가 해소되지 않는다

actions/setup-pythoncache: "pip"를 설정하고 있는 경우, requirements.txt를 업데이트해도 캐시가 남아 오래된 상태로 실행되는 경우가 있습니다. 「로컬에서는 동작하는데 CI에서만 ModuleNotFoundError가 나온다」는 경우에는 캐시가 원인인 경우가 많습니다.

# 캐시를 활성화하는 경우(requirements.txt의 해시가 변하면 자동으로 갱신됨)
- uses: actions/setup-python@v5
  with:
    python-version: "3.11"
    cache: "pip"          # requirements.txt가 변하면 자동으로 캐시를 파기

# 캐시를 비활성화하여 클린 설치하는 경우
- uses: actions/setup-python@v5
  with:
    python-version: "3.11"
    # cache를 쓰지 않으면 캐시 없음(매번 클린 설치)

원인 불명의 ModuleNotFoundError를 만났다면, GitHub의 「Actions → Caches」에서 캐시를 수동 삭제하고 재실행하는 것이 가장 빠른 해결책입니다.

📋 이 글의 정리

  • GitHub Actions.github/workflows/에 YAML을 두는 것만으로 동작하는 CI/CD 서비스로, 퍼블릭 리포지토리는 무료
  • playwright install chromium –with-deps는 CI에서 필수·잊으면 브라우저가 기동하지 않는다
  • if: always()를 아티팩트 저장 스텝에 붙이면 실패 시에도 리포트를 확인할 수 있다
  • schedule을 추가하면 매일 정해진 시간에 자동 테스트를 실행하여 서비스 품질을 지속 모니터링할 수 있다
  • matrix로 복수 브라우저의 병렬 테스트가 간단히 실현되어 크로스 브라우저 품질 보증에 효과적

CI/CD에 E2E 테스트를 통합하면 「누가 푸시해도 품질이 보증되는」 상태가 됩니다. 처음에는 Chromium만·main 브랜치만의 간단한 구성에서 시작하여 익숙해지면 스케줄 실행이나 복수 브라우저 대응으로 확장해 나가세요.

제목과 URL을 복사했습니다