Selenium만으로도 테스트 함수를 작성하고 실행할 수 있지만, 보다 구조적이고 효율적인 테스트 관리를 위해 pytest 프레임워크를 활용하였습니다. pytest를 사용한다면 다음 부분에서 장점이 있습니다.
- 테스트 실행 방식: 다양한 실행 옵션을 제공하여 테스트를 더 쉽게 실행할 수 있음.
- 테스트 구조화 및 유지보수성: fixture, parametrize 등의 기능을 통해 테스트 코드의 재사용성을 높이고 유지보수를 쉽게 할 수 있음.
- 테스트 리포트 및 디버깅: pytest-html, pytest-allure 등의 플러그인을 활용해 상세한 테스트 리포트를 생성하고, 실패한 테스트만 다시 실행할 수 있음.
예를 들어, Selenium을 단독으로 실행하는 경우, 테스트 절차를 직접 코드에 작성하고 순서대로 실행해야 하기 때문에 관리가 번거로울 수 있습니다.
Selenium 단독 실행하는 경우
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://example.com")
print(driver.title)
driver.quit()
Selenium + Pytest
위 코드를 pytest의 fixture 기능을 활용하여 더 구조적으로 변경할 수 있습니다. 아래 예제에서는 driver라는 fixture를 정의하여, 각 테스트 실행 전에 웹드라이버를 실행하고 테스트가 끝난 후 자동으로 종료하도록 설정했습니다.
import pytest
from selenium import webdriver
@pytest.fixture
def driver():
driver = webdriver.Chrome()
yield driver
driver.quit()
def test_open_page(driver):
driver.get("<https://example.com>")
assert "Example" in driver.title
Pytest에서는 fixture 를 활용하면 테스트 함수보다 위에 있든, 아래에 있든 동일하게 동작합니다. 이는 코드의 가독성과 유지보수성을 높이는 데 도움이 됩니다.
import pytest
from selenium import webdriver
def test_open_page(driver):
driver.get("https://example.com")
assert "Example" in driver.title
@pytest.fixture
def driver():
driver = webdriver.Chrome()
yield driver
driver.quit()
위처럼 pytest의 기능(fixture, parametrize, hooks)을 활용하여 코드 재사용성이 높아지고 유지보수 및 확작성에서 이점이 있습니다. 이외에도 pytest-html, pytest-allure 등의 플러그인을 활용하여 상세한 테스트 리포트 생성 기능, 병렬실행과 실패한 테스트만 다시 실행하는 기능도 존재하여 훨씬 더 효율적인 테스트가 가능해집니다.
간단하게 pytest를 사용해야하는 이유에 대해 알아보았는데요. 그렇다면 이제 실제 개발했던 코드를 기반으로 pytest를 활용한 Selenium 테스트 방법을 설명하겠습니다.
1. 프로젝트 구조 설정
아래는 pytest와 selenium을 활용한 테스트 프로젝트의 기본적인 디렉토리 구조입니다. 각 폴더와 파일의 역할은 다음과 같습니다.
- login/test/ 폴더: 테스트 코드가 위치하는 폴더
- login/ui/ 폴더: 페이지 요소와 동작을 정의하는 Page Object Model (POM) 클래스
- conftest.py: 공통 설정 (fixture)을 관리하는 파일
- pyproject.toml: 프로젝트의 패키지 종속성을 관리하는 파일 (Poetry 사용)
2. 필요한 라이브러리 설치 (Poetry 사용)
우선, 패키지 관리를 위해 poetry를 설치하고 프로젝트를 초기화 합니다. 만약 추가할 라이브러리가 있다면 pyproject.toml 에 패키지를 추가합니다.
Poetry를 활용하면 기존 requirements.txt 방식과 달리 버전 관리, 의존성 충돌 해결 및 가상 환경 관리가 한 번에 가능합니다. 패키지를 추가하려면 다음과 같이 실행하면 됩니다.
# 프로젝트에서 사용할 라이브러리 설치
poetry add selenium pytest pytest-xdist pytest-html
3. conftest.py 파일을 활용한 webdriver 설정
conftest.py 파일은 전체 디렉토리에 fixture를 제공하는 수단으로 사용됩니다. 모든 테스트 함수에서는 별도로 import하지 않아도 pytest가 자동으로 발견하여 사용합니다. 또한 scope를 설정하여 fixture의 수명을 설정하기도 합니다.
UI 자동화를 위해서는 웹을 수시로 열고 닫기를 반복하게 되는데요. 이때, 자동으로 관리하기 위해 fixture를 사용합니다. 결국, 브라우저 실행부터 종료를 자동으로 관리할 때 유용합니다.
import pytest
from selenium import webdriver
def pytest_addoption(parser):
parser.addoption("--browser", action="store", default="chrome", help="Browser to run tests on")
@pytest.fixture(scope="function")
def driver(request):
browser = request.config.getoption("--browser")
if browser == "chrome":
driver = webdriver.Chrome()
elif browser == "firefox":
driver = webdriver.Firefox()
else:
raise ValueError("Unsupported browser!")
driver.implicitly_wait(10)
yield driver
driver.quit()
위에서 정의한 driver fixture는 테스트 함수가 실행되고 끝날때까지 수명을 갖게 됩니다
4. Page Object Model (POM) 패턴이란?
Page Object Model (POM)은 웹 애플리케이션의 각 페이지를 클래스로 표현하여 테스트 코드의 유지보수성을 높이는 디자인 패턴입니다. 각 페이지의 요소와 동작을 별도의 클래스로 분리하여 관리함으로써, 테스트 코드의 중복을 줄이고 가독성을 향상시킬 수 있습니다.
POM을 사용하는 이유
- 유지보수 용이성: UI가 변경되더라도 테스트 코드가 아닌 Page Object 클래스만 수정하면 되므로 유지보수가 쉬움.
- 코드 재사용성: 여러 테스트에서 동일한 페이지 요소 및 동작을 사용할 수 있어 중복 코드가 줄어듦.
- 가독성 향상: 테스트 코드에서 직접 요소를 찾는 것이 아니라, login_page.enter_username("test_user")와 같이 명확한 동작을 수행하도록 작성할 수 있음.
5. ui/ 폴더 내 Page Object Model 구현
elements.py - 페이지 요소 관리
class LoginPageElements:
USERNAME_FIELD = "//span[contains(@class, 'SignInPage_idInput_')]/input"
PASSWORD_FIELD = "//span[contains(@class, 'SignInPage_passwordInput_')]/input"
LOGIN_BUTTON = "id=login-button"
login.py - 페이지 동작 관리 (Page Object Model)
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from .elements import LoginPageElements
class LoginPage:
# conftest.py에서 선언했던 driver fixture를 별도 선언 없이 사용
def __init__(self, driver):
self.driver = driver
def enter_username(self, username):
self.driver.find_element(By.CSS_SELECTOR, LoginPageElements.USERNAME_FIELD).send_keys(username)
def enter_password(self, password):
self.driver.find_element(By.CSS_SELECTOR, LoginPageElements.PASSWORD_FIELD).send_keys(password)
def click_login(self):
self.driver.find_element(By.CSS_SELECTOR, LoginPageElements.LOGIN_BUTTON).click()
6. 테스트 코드 작성 (test_login.py)
아래는 로그인 페이지를 테스트하는 예제 코드입니다. 로그인 페이지에서 아이디와 비밀번호를 입력한 후 로그인 버튼을 클릭하여, 성공적으로 대시보드 페이지가 로드되었는지를 검증합니다.
import pytest
from login.ui.login import LoginPage
def test_login_success(driver):
driver.get("https://example.com/login")
login_page = LoginPage(driver)
login_page.enter_username("test_user")
login_page.enter_password("password123")
login_page.click_login()
assert "Dashboard" in driver.title
자동화 테스트의 경우 테스트케이스에 스텝을 나누고 스텝마다 assert문을 사용하여 검증을 진행하였습니다.
7. 테스트 실행 방법
Poetry 환경에서 실행
poetry run pytest tests/
병렬 실행
pytest-xdist를 활용하면 여러 개의 테스트를 병렬로 실행할 수 있습니다.
poetry run pytest -n 3
HTML 리포트 생성
poetry run pytest --html=report.html
마무리
자동화를 진행하면서 실제 활용되었던 테스트 전략과 구조를 간단한 예시를 들어 설명해드렸습니다.
Page Object Model (POM) 패턴을 적용하면 테스트 코드의 재사용성을 높이고, 유지보수가 쉬워지며, UI 변경이 발생하더라도 최소한의 수정만으로 자동화 테스트를 유지할 수 있습니다.
또한 pytest를 활용하여 병렬 실행, 리포트 생성, 실패한 테스트 재시도 등의 기능을 추가하면 보다 안정적인 Selenium 테스트 환경을 구축할 수 있습니다.
다음 글에서는, 구축한 테스트 코드를 CI/CD 환경에서 어떻게 적용하고 운영할 수 있는지 자세히 알아보겠습니다 😃
'QA > 자동화' 카테고리의 다른 글
CI/CD 환경 구축기 (0) | 2025.03.03 |
---|---|
poetry 설치 및 가상환경 세팅 (0) | 2025.02.23 |
UI 자동화 도입 및 도구 선정,이렇게 시작했습니다 (0) | 2025.02.22 |