Backend/pytest

[pytest] Fixture

sangwonYoon 2023. 6. 10. 20:44

Fixture

테스팅을 하는데 있어서 필요한 조건들을 미리 준비해놓은 리소스 또는 코드

예) 특정 조건으로 구성된 데이터베이스, 데이터 셋 

 

Fixture 생성 예시

@pytest.fixture 데코레이터를 함수에 붙여 fixture로 만들 수 있다. 

import pytest


@pytest.fixture
def my_fruits():
    return ["apple", "banana", "grape", "orange"]

 

Fixture를 사용하지 않는 경우

def test1():
    fruits = ["apple", "banana", "grape", "orange"]
    assert len(fruits) == 4


def test2():
    fruits = ["apple", "banana", "grape", "orange"]
    assert "apple" in fruits

 

Fixture를 사용하는 경우

fixture를 사용할 때는 fixture의 함수명을 인자로 받아와서 사용한다.

import pytest


@pytest.fixture
def my_fruits():
    return ["apple", "banana", "grape", "orange"]


def test1(my_fruits):
    assert len(my_fruits) == 4


def test2(my_fruits):
    assert "apple" in my_fruits

 

Fixture의 특징

1. fixture는 다른 fixture를 사용할 수 있다.

import pytest


@pytest.fixture
def my_fruit():
    return "apple"


@pytest.fixture
def fruit_basket(my_fruit):
    return [my_fruit, "banana", "orange"]


def test(my_fruit, fruit_basket):
    assert myfruit in fruit_basket

 

2. fixture는 재사용이 가능하다.

서로 다른 테스트 함수에서 같은 fixture를 독립적으로 사용할 수 있다.

import pytest


@pytest.fixture
def fruit_basket():
    return ["apple", "banana", "orange"]


def test1(fruit_basket):
    fruit_basket.append("kiwi")
    assert fruit_basket == ["apple", "banana", "orange", "kiwi"]


def test2(fruit_basket):
    fruit_basket.append("melon")
    assert fruit_basket == ["apple", "banana", "orange", "melon"]

fruit_basket fixture는 각 테스트에서 호출될 때마다 실행되면서 리스트 객체의 복사본을 넘겨주고 있다.

 

3. fixture의 반환값은 같은 테스트 내에서 캐싱된다.

import pytest


@pytest.fixture
def my_fruit():
    return "apple"


@pytest.fixture
def fruit_basket():
    return []


@pytest.fixture
def append_fruit(my_fruit, fruit_basket):
    return fruit_basket.append(my_fruit)


def test_fruit(my_fruit, fruit_basket, append_fruit):
    assert fruit_basket == [my_fruit] # ["apple"] == ["apple"]

위 코드를 실행하면 어떤 결과가 나올까? fruit_basket fixture이 빈 리스트를 반환하기 때문에 테스트 실패가 나올 것이라고 생각할 수 있겠지만, 결과는 테스트 성공이다.

>> pytest test.py
=================================================== test session starts ===================================================
platform darwin -- Python 3.9.13, pytest-7.3.1, pluggy-1.0.0
rootdir: /Users/sangwon/Desktop
plugins: anyio-3.5.0
collected 1 item                                                                                                                                                  

test.py .                                                                                                            [100%]

==================================================== 1 passed in 0.01s ====================================================

위 코드에서 하나의 테스트 함수(test_fruit)에서 fruit_basket fixture는 2번 호출된다. append_fruit fixture에서 fruit_basket fixture를 호출한 뒤, my_fruit을 추가한 값을 캐싱하고 있기 때문에, test_fruit 함수에서 fruit_basket의 값은 [”apple”]을 갖는다.

반면 아래 코드의 테스트는 실패한다.

import pytest


@pytest.fixture
def my_fruit():
    return "apple"


@pytest.fixture
def fruit_basket():
    return []


@pytest.fixture
def append_fruit(my_fruit, fruit_basket):
    return fruit_basket.append(my_fruit)


def test(my_fruit, fruit_basket): # append_fruit을 호출하지 않음
    assert fruit_basket == [my_fruit] # AssertionError: assert [] == ["apple"]

 

4. autouse fixture

해당 fixture를 호출하는 함수가 없더라도 자동으로 실행되도록 하는 옵션이다.

fixture의 데코레이터에 autouse=True 인자를 넣어 autouse fixture로 만들 수 있다.

import pytest


@pytest.fixture
def my_fruit():
    return "apple"


@pytest.fixture
def fruit_basket():
    return []


# autouse fixture
@pytest.fixture(autouse=True)
def append_fruit(my_fruit, fruit_basket):
    return fruit_basket.append(my_fruit)


def test(my_fruit, fruit_basket):
    assert fruit_basket == [my_fruit] # ["apple"] == ["apple"]

 

5. scope

fixture는 테스트 함수가 호출하여 생성되고 나면 지정된 범위 내에서는 같은 fixture를 공유한다.

scope로 가질 수 있는 값은 아래와 같다.

  • function: 기본 값, 테스트가 끝나고 나면 fixture가 삭제된다.
  • class: class의 마지막 테스트가 끝나고 나면 fixture가 삭제된다.
  • module: 모듈 내의 마지막 테스트가 끝나고 나면 fixture가 삭제된다.
  • package: 패키지 내의 마지막 테스트가 끝나고 나면 fixture가 삭제된다.
  • session: 테스트 세션이 끝나고 나면 fixture가 삭제된다.
# scope 사용 예시
@pytest.fixture(scope="module")

 

6. yield

fixture로 선언된 함수는 return문으로 값을 전달할 수 있다. 이 때, 데이터를 정리하기 위한 목적으로 return 대신 yield를 사용할 수 있다.

함수에서 return이 실행되면 함수가 종료되기 때문에, yield를 대신 사용하여 yield문 뒤에 teardown code를 작성하면 테스트가 종료된 이후에 데이터를 정리할 수 있다.

from selenium.webdriver import Chrome


@pytest.fixture
def driver():
    driver = Chrome()
    yield driver

    # teardown code
    driver.quit()

 

7. conftest.py

conftest.py 파일에 정의한 fixture는 같은 디렉토리에 위치한 여러 테스트 모듈에서 import 없이 사용할 수 있다.

# test/conftest.py
import pytest


@pytest.fixture
def my_fruit():
    return "apple"


@pytest.fixture
def fruit_basket():
    return []


@pytest.fixture(autouse=True)
def append_fruit(my_fruit, fruit_basket):
    return fruit_basket.append(my_fruit)
# test/fruit_test.py
def test_fruit_sangwon(my_fruit, fruit_basket):
    assert fruit_basket == [my_fruit]