Pytest Fixture 생성

Sangyeon·2021년 3월 13일
2

Pytest

목록 보기
4/12
post-thumbnail

이 글은 Pytest Fixture 관련하여 아래 내용을 담고 있습니다.

@pytest.fixture 데커레이터를 통해 fixture 선언한 함수를 테스트 함수에서 인자로 넣어 사용할 수 있습니다.($ pytest --fixture 명령어를 통해 fixture 선언된 함수가 어떤 게 있는지 확인할 수 있습니다.)

conftest.py에 fixture 선언한 함수를 모아두면, pytest에서 테스트 실행 시 conftest.py를 거치기 때문에 여러 테스트 파일에서 해당 fixture를 사용할 수 있습니다.

argument 설정

scope 설정

fixture가 실행되는 범위에 대해 정의합니다.
설정한 scope 단위로 fixture는 한 번만 생성되고 계속 재사용됩니다.

총 5개의 scope이 있으며, 범위의 크기는 아래와 같습니다.
function(default) < class < module < package < session

  • @pytest.fixture(scope="function") : fixture가 함수 단위로 1회 생성됨(디폴트 설정으로, @pytest.fixture 와 같습니다.)
  • @pytest.fixture(scope="class") : fixture가 클래스 단위로 1회 생성됨
  • @pytest.fixture(scope="module") : fixture가 파일 단위로 1회 생성됨
  • @pytest.fixture(scope="package") : fixture가 패키지 단위로 1회 생성됨
  • @pytest.fixture(scope="session") : fixture가 test session동안 1회 생성됨

하나의 fixture 함수에서 다른 fixture 함수를 선언할 때, 범위의 크기에 맞지 않게 fixture를 선언하면(ex. function scope의 fixture 함수에 session scope의 fixture 함수 선언), scope 충돌이 발생하여 ScopeMismatch 오류가 발생합니다.

xUnit Style

pytest에서는 fixture 사용을 권장하지만, 이전 xUnit style의 setup, teardown도 지원합니다.

모듈 단위 사용법 예시는 아래와 같습니다.

def setup_module(module):
   print("setup module")

def test_1():  # pass
   assert 1 == 1

def test_2():  # fail
   assert 1 == 2

def teardown_module(module):
   print("teardown module")

위 모듈 실행 시, setup_module -> test_1 -> test_2 -> teardown_module 순으로 실행됩니다.

아래 링크에는 모듈 이외에 클래스 단위에서도 setup, teardown을 어떻게 사용하는지 나와있습니다.

참고 : https://docs.pytest.org/en/stable/xunit_setup.html

주의사항

병렬 테스트를 수행하는 pytest-xdist 플러그인을 사용한다면, fixture의 scope을 session으로 설정하여도 모든 worker에서 fixture가 실행되어 의도한 scope으로 동작하지 않습니다. 관련된 내용은 다른 포스팅에서 다룰 예정입니다.

autouse 설정

autouse=True로 설정하면, 별도 요청 없이 모든 테스트 함수에서 해당 fixture를 사용할 수 있습니다.

Sometimes you may want to have a fixture (or even several) that you know all your tests will depend on. “Autouse” fixtures are a convenient way to make all tests automatically request them. This can cut out a lot of redundant requests, and can even provide more advanced fixture usage (more on that further down).

예를 들어, hook 함수에서 다른 파일의 fixture를 사용해야 하는 경우, 해당 fixture에 @pytest.fixture 인자로 autouse=True를 사용하면, hook 함수에서 해당 fixture를 인식할 수 있습니다.

conftest.py

def pytest_addoption(parser):
    parser.addoption("--param-type", action="store", default=False, help="")

@pytest.fixture(autouse=True)
def param_type(request):
    return request.config.getoption("--param-type")

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    param_type = item.funcargs["param_type"]
    if param_type:
      ~~

활용 예시

예시1 - 웹 드라이버 세팅

fixture를 활용한 첫 번째 예시는 웹 드라이버 실행하는 것을 fixture로 선언해두고, 테스트 수행 시, 웹 드라이버를 사용할 때마다 해당 fixture를 이용하는 것입니다.(예시에서는 크롬 브라우저 웹 드라이버로 selenium을 사용합니다.)

1) conftest.py 에서 fixture를 선언합니다.

from selenium import webdriver
import pytest

@pytest.fixture(scope="session")
def driver():
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('--headless')
    driver = webdriver.Chrome("chromedriver경로", options=chrome_options)
    yield driver
    driver.quit()

참고로, return 대신 yield fixture를 사용하면 리턴 기능을 하면서 yield 이후 명령문은 해당 fixture 사용이 종료된 후 수행됩니다. (yield 이후 명령문을 teardown 코드라고 볼 수 있습니다)

There is a special usage of yield statement in Pytest that allows you to execute the fixture after all the test functions. The code before yield serves as the setup code, and the code after yield serves as the teardown code.

2) test_example.py 에서 fixture인 driver를 매개변수로 테스트 코드를 작성합니다.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


def test_1(driver):
    url = "https://www.google.com"
    driver.get(url)
    assert driver.title == "Google"


def test_2(driver):
    login_btn = driver.find_element_by_id('gb_70')
    login_btn.click()
    title_element = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.ID, 'logo')))
    title = title_element.get_attribute('title')
    assert title == "Google"

예시 테스트 코드는 간단하게 구글 사이트의 브라우저 타이틀과 로그인 페이지로 이동 시 타이틀이 Google이 맞는지 확인하는 것으로 작성하였습니다.

예시2 - 커맨드라인 옵션 추가

fixture를 활용한 두 번째 예시는 환경에 대한 커맨드라인 옵션을 추가하여 테스트를 수행하는 환경에 따라 테스트를 수행하도록 하는 것입니다.

1) conftest.py에 pytest의 커맨드라인 옵션을 추가하고, fixture를 선언합니다.

def pytest_addoption(parser):
    parser.addoption("--env", action="store", default=None, help="dev/real")

@pytest.fixture(scope="session")
def env(request):
    return request.config.getoption("--env")

예시에서는 환경 옵션 입력 없이 default로 잘못 수행할 경우를 대비하여 default=None으로 설정해두었는데, default='real'로 옵션 입력 없으면 리얼 환경에서 수행하게 설정할 수도 있습니다.

2) test_example.py 에서 fixture인 driver를 매개변수로 테스트 코드를 작성합니다. (구글의 개발 환경 사이트가 dev-google로 가정합니다.)

def test_title(driver, env):
    if env == 'real'
        url = "https://www.google.com"
    elif env == 'dev'
    	url = "https://www.dev-google.com"
    driver.get(url)
    assert driver.title == "Google"

3) 수행하는 환경에 맞게 옵션을 넣어 테스트를 수행합니다.

리얼 환경에서 테스트하는 경우,

$ pytest test_example.py --env=real

개발 환경에서 테스트하는 경우,

$ pytest test_example.py --env=dev

Reference

https://docs.pytest.org/en/latest/how-to/fixtures.html

profile
I'm a constant learner.

0개의 댓글