day_by_day

Page Object Model 본문

QA Automation/그냥하는거지무슨주제가있어

Page Object Model

kokorii_ 2024. 9. 21. 20:48

팀원들과 함께 스터디를 하며 QA팀 전체가 실무 자동화 스킬을 익힐 수 있도록 지원하고 있다.

스터디도 진행하고, 여러가지 아티클이나 정보글을 공유하기도하는데 이중 내가 가장 먼저 작성했던 POM에 대한 내용을 옮겨본다.

내부공유자료 전체는 아니고 일부 더 붙여서(...) 남겨보기


  • Page Object Model (POM)은 소프트웨어 테스트 자동화에 사용되는 디자인 패턴
  • 테스트 코드와 페이지의 구조를 모듈화하여 관리하는 방법
  • 웹 애플리케이션 테스트에 널리 사용되지만, 다른 유형의 자동화 테스트에서도 적용됨

1. key idea:

  • 테스트 코드와 웹 페이지의 구조를 분리하여 관리하는 것
  • 각 웹 페이지는 해당 페이지의 동작과 관련된 메서드를 가지는 페이지 객체로 나타내며, 테스트 케이스는 이러한 페이지 객체를 활용하여 테스트를 작성

2. 주요 구성 요소:

  • Page Objects (페이지 객체): 각 페이지를 나타내는 객체(class)
    • 페이지 내 요소와 상호작용하는 메서드 포함.
    • 클래스 추상화 가능
  • Test Scripts (테스트 스크립트): 실제로 테스트를 수행하는 코드(.py)
  • 프로젝트 구조
    • POM프로젝트의 대부분은 다음과 같이 구성되어 있지만, configure 등은 임의지정가능하다.
    • 난 귀찮아서 .. 따로 폴더만들지 않았는데 이제 슬슬 파일이 많아지면서 만들어야 하나 고민중이다
    • log폴더나 output 폴더는 내가 임의로 생성한 폴더 구조다. 프로젝트를하면서 없기는 어려운 폴더들이라 기본구조로 지정했다. (나만의 기본구조..)

 

project_root/
|-- logs/
|-- output/
|	|-- screenshot/
|-- pages/
|    |-- __init__.py
|    |-- login_page.py
|    |-- contact_page.py
|-- tests/
|    |-- __init__.py
|    |-- test_login.py
|    |-- test_logout.py
|    |-- test_contact.py
|-- common/
|    |-- __init__.py
|    |-- generate_data.py
|    |-- calendar_module.py
|-- requirements.txt
|-- pytest.ini
|-- configure.json
|-- test_run.sh

3. 기본 원칙:

  • 단일 책임 원칙 (Single Responsibility Principle): 각 페이지 객체는 특정 웹 페이지 또는 기능에 대해 책임을 갖는다. 페이지 객체의 목적은 해당 페이지와 상호작용하고 필요한 동작을 정의하는 것
  • 재사용성과 모듈화: 페이지 객체는 재사용 가능한 모듈로서 설계해야 한다.
    • 한 페이지의 변경이 다른 페이지나 테스트 코드에 미치는 영향을 최소화해야함
  • 테스트 간 상호 의존성을 최소화 할 것!! 
    • 실제 만들다보면 이부분이 귀찮고 까다롭게 느껴질때가 많다. 
    • ex) 페이지 이탈 시 재진입 시 마지막 이탈 페이지에서 시작해야하는 등.. 

4. POM의 장점:

  • 유지보수성 향상: 웹 페이지 구조의 변경이나 새로운 기능 추가 시, 해당 페이지 객체만 수정하면 되므로 유지보수성 향상
  • 가독성: 테스트 코드가 간결하게 유지됨
  • 테스트 가능성: 테스트 케이스는 각 페이지 객체의 메서드를 호출하여 특정 동작을 수행하고, 페이지 객체는 웹 페이지와 상호작용하는 데 집중

5. POM을 적용한 playwright 구조 예시

5-1. pages/login_page.py

  • 페이지에 있는 기능(버튼, 이미지 등)을 작성
from playwright.sync_api import Page

class LoginPage:
    def __init__(self, page: Page):
        self.page = page
        self.case_num = 0

    def navigate_to_login_page(self):
        self.page.goto("https://example.com/login")

    def enter_username(self, username):
        self.page.fill.locator("username")

    def enter_password(self, password):
        self.page.fill('input[name="password"]', password)

    def click_login_button(self):
        self.page.get_by_role(role="button", name="login").click()

5-2. tests/test_login.py

  • login_page.py 를 이용해서 login 페이지의 동작을 검증
  • 웹 페이지를 그대로 위 page.py에 옮긴 것이고, 이를 이용해 다양한 시나리오 검증 가능
  • 테스트끼리 상호 의존성이 없도록 작성하는 것이 매우 중요
from playwright.sync_api import sync_playwright
from pages.login_page import LoginPage

def test_login():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        context = browser.new_context()
        page = context.new_page()

        # LoginPage 객체 생성
        login_page = LoginPage(page)

        # LoginPage의 메서드를 사용하여 로그인 시나리오 수행
        login_page.navigate_to_login_page()
        login_page.enter_username("test_user")
        login_page.enter_password("password123")
        login_page.click_login_button()

        # 로그인 결과 검증 및 기타 테스트 로직 수행

        context.close()

위 내용 정도만 알고 있으면 프로젝트 시작에 문제가 없다.

새로운 인력의 온보딩이나 팀 세팅시에 위 내용들을 시작으로 상호간 지식레벨을 공유하는 게 좋겠다. 

이 지식을 알고 있는가? 가 아니라 이 정보를 받아들이는 태도가 중요하다 할 수 있겠다.
(모르는 것을 질문하는 용기와, 더 필요한 것을 피드백할 수 있는 커뮤니케이션)

물론 pom을 아는것과 pytest-playwright를 아는것은 다른문제