python pytest!

pytest

https://docs.pytest.org/en/stable/

def inc(x):
    return x + 1


def test_answer():
    assert inc(3) == 5

함수, 파일명에 _test, test_ 클래스명에 Test... 문자열이 들어가 있으면 pytest 가 자동으로 테스트 함수임을 감지한다.

pytest --version
# pytest 8.3.1

pytest my_python.py
# ================== test session starts ===================
# platform darwin -- Python 3.10.8, pytest-8.3.1, pluggy-1.5.0
# rootdir: /Users/gojiyong/PycharmProjects/pythonProject
# collected 0 items                                                                      

# ================= no tests ran in 0.00s ==================
# ERROR: file or directory not found: my_python.py

@pytest.mark.parametrize

여러개의 입력 파라미터로 지정할 수 있다.

@pytest.mark.parametrize("input, expected", [
    (1, 2),
    (2, 3),
    (3, 4),
])
def test_increment(input, expected):
    assert input + 1 == expected

@pytest.mark.skip

테스트 진행하지 않고 skip

@pytest.mark.skip(reason="not implemented yet")
def test_not_implemented():
    assert False

@pytest.mark.skipif

skip_flag = True

@pytest.mark.skipif(skip_flag, reason="feature flag is disabled")
def test_skip_flag():
    assert True


@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires Python 3.7 or higher")
def test_python_version():
    assert True


@pytest.mark.skipif(os.getenv('SKIP_TEST') == 'true', reason="SKIP_TEST environment variable is set")
def test_skip_based_on_env_var():
    assert True

외부 의존성에 맞춰 테스트

def is_service_available():
    try:
        response = requests.get("https://example.com")
        return response.status_code == 200
    except requests.RequestException:
        return False

@pytest.mark.skipif(not is_service_available(), reason="external service is not available")
def test_external_service():
    assert True

@pytest.fixture

모든 테스트에 필요한 공통 설정을 입력 파라미터로 지정할 수 있다.
@pytest.fixture scope 별로 공통설정에서 반환한 값이 공유될 수 있다.

# 공통 설정 정의하기
@pytest.fixture
def sample_data():
    return {"key": "value"}

def test_sample_data(sample_data):
    assert sample_data["key"] == "value"

# 여러 공통 설정가져오기
@pytest.fixture
def user():
    return {"username": "test_user"}

@pytest.fixture
def password():
    return "secure_password"

def test_user_login(user, password):
    assert user["username"] == "test_user"
    assert password == "secure_password"

# 자동으로 모든 공통코드 수행하기
@pytest.fixture(autouse=True)
def auto_setup():
    print("Automatically setting up")

def test_auto_setup_1():
    assert True

def test_auto_setup_2():
    assert True
# 공통 설정 연계
@pytest.fixture
def username():
    return "test_user"

@pytest.fixture
def user(username):
    return {"username": username}

def test_user(user):
    assert user["username"] == "test_user"

fixture 와 yield

테스트 수행시 공통적인 setup & teardown(설정 & 해제) 작업을 @pytest.fixture 를 통해 정의할 수 있다.

file 열기, file 반남 형태의 테스트를 수행할 때 자주 사용함.

@pytest.fixture
def setup_and_teardown():
    # setup code
    data = {"key": "value"}
    yield data # 테스트 함수에 data 전달
    # teardown code
    print("Teardown")

def test_setup_and_teardown(setup_and_teardown):
    assert setup_and_teardown["key"] == "value"

setup code 에서 데이터를 가져오고, 테스트가 수행 완료된 후 teardown code 를 수행하여 후처리를 진행한다.

fixture scope

@pytest.fixture scope 옵션은 네 가지 범위를 지원한다.

  • function (기본값): 함수 내에서만 사용 가능
  • class: 클래스 내부 정의된 테스트 함수간 공유
  • module: 파일내부 정의된 테스트 함수간 공유
  • package: 패키지(폴더)내부 정의된 테스트 함수간 공유
  • session: 테스트 진행동안 공유
# scope=function 함수가 종료되면 파괴됨
@pytest.fixture(scope="function")
def resource():
    return []

def test_function_scope_1(resource):
    resource.append(1)
    assert resource == [1]

def test_function_scope_2(resource):
    resource.append(2)
    assert resource == [2]
# scope=class, 클래스 외부로 나가면 파괴됨
@pytest.fixture(scope="class")
def resource():
    return []

class TestClassScope:
    def test_class_scope_1(self, resource):
        resource.append(1)
        assert resource == [1]

    def test_class_scope_2(self, resource):
        resource.append(2)
        assert resource == [1, 2]
# scope=module, 모듈 외부로 나가면 파괴됨
@pytest.fixture(scope="module")
def resource():
    return []

def test_module_scope_1(resource):
    resource.append(1)
    assert resource == [1]

def test_module_scope_2(resource):
    resource.append(2)
    assert resource == [1, 2]

@pytest.mark.usefixtures

테스트 함수 매개변수로 @pytest.fixture 로 정의한 공용설정 메서드를 삽입해야 했지만
@pytest.mark.usefixtures 를 사용하면 테스트 함수 위에 decorator 만 추가해주면 된다.

대신 공통 설정에 대한 값은 가져올 수 없다.

# 테스트 함수에서 사용하기
@pytest.fixture
def setup_data():
    print("Setting up data")

@pytest.mark.usefixtures("setup_data")
def test_example():
    assert True  # Fixture setup_data is used, but not directly referenced in the function

# 클래스 테스트 함수에서 사용하기
@pytest.fixture
def setup_data():
    print("Setting up data")

@pytest.fixture
def setup_database():
    print("Setting up database")

@pytest.mark.usefixtures("setup_data", "setup_database")
class TestMultipleFixtures:
    def test_method_1(self):
        assert True

    def test_method_2(self):
        assert True

pytest-timeout

pip install pytest-timeout

pytest.ini 파일 설정 하여 모든 테스트에 timeout 관련 설정을 할 수 있다.

[pytest]
markers =
    slow: 느린 테스트
timeout = 5  # 기본 타임아웃 설정 (필요 시)

@pytest.mark.timeout

해당 시간안에 테스트가 종료되지 않을 경우 오류로 판단.

@pytest.mark.timeout(2)
def test_with_timeout():
    time.sleep(1)  # 이 테스트는 성공
    assert True


@pytest.mark.timeout(2)
def test_with_timeout_failure():
    time.sleep(3)  # 이 테스트는 실패
    assert True

@pytest.mark.xfail

실패해도 결과가 실패로 간주되지 않는다

실패했을 때 failed 로 처리되지 않고 ignored 로 처리됨

@pytest.mark.xfail
def test_not_yet_implemented():
    assert False

카테고리:

업데이트: