Python에서 단위 테스트를 위한 PyTest 사용 방법

코드를 Python으로 작성할 때, 코드가 예상대로 작동하는지 확인하는 것이 중요합니다. 이를 위한 가장 좋은 방법 중 하나는 유닛 테스트를 사용하는 것입니다. 유닛 테스트는 코드의 작은 부분(또는 유닛)이 제대로 작동하는지 확인하는 데 도움을 줍니다.

이번 기사에서는 PyTest를 사용하여 Python에서 효과적인 유닛 테스트를 작성하고 실행하는 방법을 배울 것입니다.

유닛 테스트란 무엇인가요?

유닛 테스트는 단일 함수나 작은 코드 조각을 검사하는 데 초점을 맞춘 작고 간단한 테스트입니다. 이들은 코드가 예상대로 작동하는지 확인하고 초기 단계에서 버그를 잡는 데 도움을 줍니다.

유닛 테스트는 함수, 메서드, 심지어 클래스와 같은 코드의 다양한 부분에 대해 작성할 수 있습니다. 유닛 테스트를 작성함으로써 전체 프로그램을 실행하지 않고도 코드를 테스트할 수 있습니다.

왜 PyTest를 사용하나요?

PyTest는 Python을 위한 인기 있는 테스트 프레임워크로, 테스트를 쉽게 작성하고 실행할 수 있게 해줍니다.

사용이 간편하고 다음과 같은 많은 유용한 기능을 가지고 있습니다:

  • 간단하고 명확한 테스트 케이스를 작성할 수 있습니다.
  • fixture, 매개변수화된 테스트 및 플러그인과 같은 고급 기능을 제공합니다.
  • 다른 테스트 도구 및 라이브러리와 잘 작동합니다.
  • 읽기 쉬운 테스트 결과 및 보고서를 생성합니다.

리눅스에서 PyTest 설정하기

테스트 작성을 시작하기 전에 PyTest를 설치해야 합니다. PyTest가 설치되어 있지 않다면, pip라는 파이썬 패키지 관리자를 사용하여 설치할 수 있습니다.

pip install pytest

PyTest가 설치되면, 테스트 작성을 시작할 준비가 된 것입니다!

PyTest로 첫 번째 테스트 작성하기

간단한 함수를 작성한 다음, 그에 대한 테스트를 작성해 보겠습니다.

1단계: 간단한 함수 작성하기

먼저, 테스트하고자 하는 파이썬 함수를 만들어 보겠습니다. 두 개의 숫자를 더하는 함수가 있다고 가정해봅시다:

# add.py
def add(a, b):
    return a + b

이것은 두 개의 숫자 a와 b를 받아서 더하고 결과를 반환하는 간단한 함수입니다.

2단계: 함수에 대한 테스트 작성하기

이제 add 함수에 대한 테스트를 작성해 보겠습니다. PyTest에서는 테스트가 별도의 파일에 작성되며, 일반적으로 테스트 파일을 쉽게 식별할 수 있도록 test_*.py라는 이름을 사용합니다.

새 파일을 test_add.py라는 이름으로 만들고 다음 테스트 코드를 작성합니다:

# test_add.py
from add import add

def test_add_numbers():
    assert add(2, 3) == 5
    assert add(-1, 1) == 0
    assert add(0, 0) == 0

위 코드 설명:

  • 우리는 add 함수를 add.py 파일에서 가져옵니다.
  • 우리는 test_add_numbers()라는 테스트 함수를 정의합니다. PyTest에서 테스트 함수는 test_라는 단어로 시작해야 합니다.
  • 테스트 함수 내부에서 우리는 assert 문을 사용하여 add 함수 호출 결과가 예상 값과 일치하는지 확인합니다. assert 문 내의 조건이 True이면 테스트가 통과되고, 그렇지 않으면 실패합니다.

3단계: 테스트 실행

테스트를 실행하려면 터미널을 열고 test_add.py 파일이 위치한 디렉토리로 이동한 다음 다음 명령어를 실행하세요:

pytest

PyTest는 자동으로 모든 테스트 파일(이름이 test_로 시작하는 파일)을 찾아 내부의 테스트를 실행합니다. 모든 것이 올바르게 작동하면 다음과 같은 출력이 나타납니다:

Verifying Python Code Functionality

(.)은 테스트가 통과했음을 나타냅니다. 문제가 발생한 경우 PyTest가 오류 메시지를 표시합니다.

더욱 고급 테스트 작성하기

기본 테스트를 작성하고 실행하는 방법을 알았으니, PyTest의 더 고급 기능들을 살펴보겠습니다.

예상되는 예외 테스트하기

때때로 코드가 잘못될 때 올바른 예외가 발생하는지 테스트하고 싶을 수 있습니다. 이는 pytest.raises() 함수를 사용하여 수행할 수 있습니다.

예를 들어, 두 숫자를 나누는 함수를 테스트하고 싶다고 가정해봅시다. 두 번째 숫자가 제로일 경우(0으로 나누기 오류를 피하기 위해) 예외를 발생시키고자 합니다.

여기에 divide 함수가 있습니다:

# divide.py
def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

이제 제로로 나누려고 할 때 ValueError가 발생하는지 확인하는 이 함수에 대한 테스트를 작성해 보겠습니다:

# test_divide.py
from divide import divide
import pytest

def test_divide_numbers():
    assert divide(10, 2) == 5
    assert divide(-10, 2) == -5
    assert divide(10, -2) == -5

def test_divide_by_zero():
    with pytest.raises(ValueError):
        divide(10, 0)

코드 설명:

  • 우리는 test_divide_by_zero()라는 새로운 테스트 함수를 추가했습니다.
  • 이 함수 내부에서는 pytest.raises(ValueError)를 사용하여 두 번째 인수로 0을 사용하여 나누기 함수를 호출할 때 ValueError가 발생하는지 확인합니다.

테스트를 다시 실행하려면 pytest 명령을 사용하세요. 모든 것이 제대로 작동하면 다음과 같은 출력이 표시됩니다:

Test Your Code with PyTest

설정 및 정리를 위한 픽스쳐 사용하기

일부 경우에는 테스트를 실행하기 전에 특정 조건을 설정하거나 테스트가 끝난 후 정리해야 할 필요가 있습니다. PyTest는 이를 처리하기 위한 픽스쳐를 제공합니다.

픽스쳐는 테스트를 위한 조건을 설정하거나 해체하는 데 사용할 수 있는 함수입니다. 픽스쳐는 테스트에 필요한 객체를 생성하거나 데이터베이스에 연결하는 데 자주 사용됩니다.

다음은 파일 작업 테스트를 위해 임시 디렉토리를 설정하는 픽스쳐 사용의 예입니다:

# test_file_operations.py
import pytest
import os

@pytest.fixture
def temporary_directory():
    temp_dir = "temp_dir"
    os.mkdir(temp_dir)
    yield temp_dir  # This is where the test will run
    os.rmdir(temp_dir)  # Cleanup after the test

def test_create_file(temporary_directory):
    file_path = os.path.join(temporary_directory, "test_file.txt")
    with open(file_path, "w") as f:
        f.write("Hello, world!")
    
    assert os.path.exists(file_path)

코드 설명:

  • 우리는 테스트 전에 임시 디렉토리를 생성하고 이후에 삭제하는 temporary_directory라는 픽스쳐를 정의합니다.
  • 테스트 함수 test_create_file()는 이 픽스쳐를 사용하여 임시 디렉토리에 파일을 생성하고 파일이 존재하는지 확인합니다.

pytest 명령으로 테스트를 다시 실행하세요. PyTest는 자동으로 픽스쳐를 감지하고 사용할 것입니다.

Pytest로 테스트 매개변수화하기

때때로 다른 입력으로 동일한 테스트를 실행하고 싶을 때가 있습니다. PyTest는 매개변수를 사용하여 이를 쉽게 할 수 있도록 해줍니다.

여러 쌍의 숫자로 add 함수를 테스트하고 싶다고 가정해 봅시다. 각 쌍에 대해 별도의 테스트 함수를 작성하는 대신, pytest.mark.parametrize를 사용하여 서로 다른 입력으로 동일한 테스트를 실행할 수 있습니다.

# test_add.py
import pytest
from add import add

@pytest.mark.parametrize("a, b, expected", [
    (2, 3, 5),
    (-1, 1, 0),
    (0, 0, 0),
    (100, 200, 300)
])
def test_add_numbers(a, b, expected):
    assert add(a, b) == expected

코드 설명:

  • pytest.mark.parametrize 데코레이터를 사용하여 여러 입력 세트(a, b, expected)를 정의합니다.
  • 테스트 function test_add_numbers()는 각 입력 세트에 대해 한 번씩 실행됩니다.

pytest 명령으로 테스트를 다시 실행하면, 각 입력 세트에 대해 한 번씩 총 네 번 테스트가 실행됩니다.

결론

이 기사에서는 Python에서 PyTest를 사용하여 효과적인 단위 테스트를 작성하고 실행하는 방법을 배워, 버그를 조기에 잡고 코드가 예상대로 작동하는지 확인했습니다.

PyTest는 이러한 테스트를 쉽게 작성하고 실행할 수 있게 해주며, 강력한 기능을 통해 Python 여정을 진행하면서 더 복잡한 테스트 요구 사항도 처리할 수 있습니다.

Source:
https://www.tecmint.com/unit-testing-python-code-with-pytest/