Akashic Records

Code Test(Unit Test) with pytest 본문

Python for Beginners

Code Test(Unit Test) with pytest

Andrew's Akashic Records 2024. 6. 3. 17:53
728x90

python for beginners

파이썬 코드를 테스트하는 방법은 여러 가지가 있습니다. 여기 몇 가지 주요 방법을 소개하겠습니다:

 

1. 인터프리터를 사용한 수동 테스트

가장 간단한 방법은 파이썬 인터프리터를 사용하여 코드를 한 줄씩 실행해 보는 것입니다. 이 방법은 작은 코드 조각이나 단순한 함수를 빠르게 테스트할 때 유용합니다.

 

2. 스크립트 실행

파이썬 코드를 파일에 저장하고, 명령줄에서 python 파일명.py와 같이 실행하여 전체 스크립트를 테스트할 수 있습니다. 이 방법은 코드 전체의 흐름을 확인하고자 할 때 적합합니다.

 

3. assert 문을 사용한 간단한 테스트

assert 문을 사용하여 특정 조건이 참인지 확인함으로써 코드가 예상대로 동작하는지 테스트할 수 있습니다. 예를 들어, 함수의 결과가 기대한 값과 같은지 검증할 수 있습니다.

def add(a, b):
    return a + b

assert add(2, 3) == 5  # 이 조건이 참이면 아무 일도 일어나지 않습니다.
assert add(2, 2) == 5  # 이 조건이 거짓이면 AssertionError가 발생합니다.

 

4. unittest 모듈 사용

Python의 내장 모듈인 unittest를 사용하여 보다 체계적인 단위 테스트를 작성할 수 있습니다. 테스트 케이스를 클래스로 구성하고, 각 테스트 함수 안에서 여러 조건을 검증할 수 있습니다.

import unittest

class TestAddFunction(unittest.TestCase):
    def test_add(self):
        self.assertEqual(add(2, 3), 5)
        self.assertEqual(add(0, 0), 0)

if __name__ == '__main__':
    unittest.main()

 

5. pytest 라이브러리 사용

pytest는 Python에서 가장 널리 사용되는 테스팅 프레임워크 중 하나로, 간결하고 사용하기 쉬운 API를 제공합니다. pytest를 사용하면 복잡한 테스트 시나리오도 쉽게 구성할 수 있습니다.

pip install pytest

테스트 파일을 작성한 후, 명령줄에서 pytest를 실행하면 자동으로 테스트를 찾아 실행합니다.

pytest 이해하기

pytest는 파이썬에서 사용되는 강력한 테스팅 프레임워크 중 하나로, 복잡하지 않으면서도 매우 유연하고 사용하기 쉬운 API를 제공합니다. 이는 단위 테스트에서부터 복잡한 함수적 테스트까지 다양한 테스트를 쉽게 구성하고 실행할 수 있도록 설계되었습니다. 다음은 pytest의 주요 특징과 사용 방법에 대한 설명입니다.

 

주요 특징

  1. 간결한 테스트 코드: pytest는 기본적으로 파이썬의 assert문을 사용하여 간단하게 테스트를 작성할 수 있습니다.
  2. 자동 테스트 발견: 테스트 파일과 함수를 자동으로 인식합니다. 보통 test_로 시작하는 파일과 함수를 테스트로 취급합니다.
  3. 풍부한 플러그인 생태계: 다양한 플러그인을 통해 pytest의 기능을 확장할 수 있으며, 커버리지 보고서, 병렬 테스트 실행 등 다양한 기능을 추가할 수 있습니다.
  4. 고급 픽스처 지원: 복잡한 리소스 준비 및 정리를 위한 픽스처를 정의할 수 있으며, 이는 테스트 코드의 재사용을 높여 줍니다.
  5. 매개변수화된 테스트: 단일 테스트 함수로 다양한 입력을 받아 여러 케이스를 테스트할 수 있는 기능을 제공합니다.

기본 사용법

1. 설치:

pip install pytest

 

2. 테스트 코드 작성:
일반적으로 test_로 시작하는 파일 이름에 테스트를 작성합니다. 테스트 함수 역시 test_로 시작해야 pytest가 자동으로 인식합니다.

# 파일: test_example.py 

def add(a, b): 
    return a + b 

def test_add():
    assert add(2, 3) == 5 
    assert add(4, 4) == 8

 

3. 테스트 실행:
터미널에서 pytest 명령어를 사용하여 테스트를 실행할 수 있습니다. 이 명령은 모든 테스트 파일을 찾아 테스트를 수행합니다.

 

고급 기능

- 픽스처: 테스트 전에 설정해야 할 객체나 상태를 정의할 수 있으며, 테스트 후에 정리 작업도 할 수 있습니다.

import pytest
@pytest.fixture
def input_value():
    return 39

def test_divisible_by_3(input_value):
    assert input_value % 3 == 0

 

- 매개변수화된 테스트: 하나의 테스트 함수로 여러 데이터 세트를 테스트할 수 있습니다.

import pytest

@pytest.mark.parametrize("num, output", [(1, 10), (2, 20), (3, 30)])
def test_multiplication_10(num, output):
    assert 10 * num == output

 

pytest 동작 방식

pytest는 파이썬 테스트 코드를 실행하고 결과를 평가하는 테스트 프레임워크로, 강력한 기능과 간결함을 통해 많은 파이썬 개발자들에게 사랑받고 있습니다. pytest의 동작 방식을 이해하려면 몇 가지 주요 구성 요소와 과정을 살펴볼 필요가 있습니다:

 

1. 테스트 발견

pytest는 특정 규칙에 따라 테스트 파일과 테스트 함수를 자동으로 찾습니다:

  • 파일 이름이 test_로 시작하거나 _test로 끝나야 합니다.
  • 테스트 함수는 test로 시작해야 합니다.

이러한 규칙은 pytest가 코드베이스에서 테스트를 효율적으로 찾도록 돕습니다.

 

2. 테스트 실행

테스트 실행은 아래와 같은 순서로 진행됩니다:

  • 테스트 수집: pytest는 지정된 디렉토리(기본적으로 현재 디렉토리)에서 테스트 파일을 찾고, 테스트 함수와 클래스를 수집합니다.
  • 테스트 실행: 각 테스트는 독립적인 환경에서 실행됩니다. 테스트가 실패하면, pytest는 에러 메시지와 스택 트레이스를 제공하여 디버깅을 도울 수 있습니다.

3. 픽스처

pytest의 핵심 기능 중 하나는 픽스처입니다. 픽스처는 테스트 함수를 위해 필요한 데이터나 상태를 설정하고, 테스트 후에 정리(cleanup) 작업을 수행할 수 있는 기능을 제공합니다. 픽스처는 @pytest.fixture 데코레이터를 사용하여 정의하며, 필요한 테스트 함수에 인자로 전달됩니다.

 

4. 단언(assertions)

pytest는 파이썬의 기본 assert 문을 사용하여 테스트 조건을 검증합니다. assert가 실패하면, pytest는 실패의 원인이 되는 표현식과 관련 값을 출력하여 문제를 쉽게 파악할 수 있도록 돕습니다.

 

5. 결과 보고

테스트 실행 후, pytest는 성공, 실패, 스킵된 테스트의 개수와 함께 상세한 결과 보고서를 제공합니다. 필요한 경우 -v (verbose) 옵션을 사용하여 보다 상세한 출력을 얻을 수 있습니다.

6. 확장성

pytest는 플러그인을 통해 확장할 수 있으며, 많은 타사 플러그인들이 존재합니다. 이를 통해 데이터베이스 픽스처, 웹 테스트, 코드 커버리지 보고 등 다양한 기능을 추가할 수 있습니다.

이러한 방식으로 pytest는 파이썬 개발 환경에서 효율적이고 유연한 테스트 솔루션을 제공하며, 복잡한 테스트 요구 사항을 쉽게 처리할 수 있도록 도와줍니다. 테스트 코드의 품질을 높이고, 개발 과정을 더욱 견고하게 만드는 데 큰 역할을 합니다.

Assertion 구조

파이썬에서 테스트를 수행할 때 사용하는 assert 문은 테스트 코드에서 조건의 참과 거짓을 검증하는 간단하면서도 강력한 도구입니다. 기본적으로 파이썬의 assert 문은 단 하나의 형식만을 가지고 있으나, 다양한 조건과 표현을 사용하여 여러 상황을 검증할 수 있습니다.

 

기본 구조

assert 조건, "메시지"
  • 조건: 테스트하고자 하는 조건입니다. 이 조건이 False로 평가될 때, AssertionError가 발생합니다.
  • 메시지: 선택적입니다. 조건이 False일 때 출력되는 에러 메시지입니다.

assert의 사용 예시

 

1. 값의 동등성 검증

함수의 반환 값이 예상한 값과 같은지 검증합니다.

def add(a, b):
    return a + b

assert add(2, 3) == 5, "2와 3을 더하면 5가 나와야 합니다"

 

2. 값의 비동등성 검증

두 값이 다른지 확인합니다.

def subtract(a, b):
    return a - b

assert subtract(5, 3) != 3, "5에서 3을 뺀 결과는 3이 아닙니다"

 

3. 참과 거짓 검증

특정 변수나 표현식의 참, 거짓을 직접 검증합니다.

flag = True
assert flag, "Flag는 True여야 합니다"

result = None
assert not result, "Result는 None이거나 False여야 합니다"

 

4. 리스트, 튜플, 딕셔너리의 포함 관계 검증

특정 요소가 컨테이너 타입 안에 포함되어 있는지 확인합니다.

list_of_numbers = [1, 2, 3, 4, 5]
assert 5 in list_of_numbers, "리스트에 5가 포함되어 있어야 합니다"

user_data = {"username": "john", "email": "john@example.com"}
assert "username" in user_data, "user_data에 'username' 키가 있어야 합니다"

 

5. 예외 발생 검증

특정 조건에서 예외가 발생하는지 확인합니다. 이 경우 직접 assert를 사용하는 것보다 pytest에서 제공하는 pytest.raises를 사용하는 것이 일반적입니다.

def divide(a, b):
    return a / b

import pytest
with pytest.raises(ZeroDivisionError):
    divide(1, 0)

이러한 다양한 조건을 활용하여 assert 문을 통해 코드가 정확하게 동작하는지 체계적으로 검증할 수 있습니다. 테스트 코드에서는 이러한 assert들을 사용하여 함수나 메소드의 각각의 동작을 다양한 조건하에 검증하게 됩니다.

클래스 테스트

클래스를 테스트하는 것은 객체 지향 프로그래밍에서 중요한 부분입니다. 클래스의 메서드들이 예상대로 작동하는지 확인하고, 클래스 인스턴스의 상태 관리가 적절하게 이루어지는지 검증해야 합니다. 일반적으로 클래스를 테스트하기 위해 파이썬의 unittest 프레임워크나 pytest를 사용할 수 있습니다. 여기서는 pytest를 사용한 예시를 들어 설명하겠습니다.

 

1. 기본 클래스 정의

먼저 테스트할 클래스를 정의합니다. 예를 들어, 간단한 은행 계좌 클래스를 만들어 보겠습니다. 이 클래스는 잔액을 관리하고, 입금과 출금 기능을 가지고 있습니다.

class BankAccount:
    def __init__(self, initial_balance=0):
        self.balance = initial_balance

    def deposit(self, amount):
        if amount > 0:
            self.balance += amount
            return self.balance
        else:
            raise ValueError("Deposit amount must be positive")

    def withdraw(self, amount):
        if amount > 0 and amount <= self.balance:
            self.balance -= amount
            return self.balance
        else:
            raise ValueError("Invalid withdrawal amount")

 

2. 테스트 클래스 작성

이제 pytest를 사용하여 BankAccount 클래스를 테스트하는 코드를 작성합니다. 테스트 케이스를 구성하여 클래스의 기능들이 정상적으로 동작하는지 확인합니다.

import pytest

class TestBankAccount:
    def test_initial_balance(self):
        # 초기 잔액 테스트
        account = BankAccount()
        assert account.balance == 0, "Initial balance should be zero"

        account_with_balance = BankAccount(100)
        assert account_with_balance.balance == 100, "Initial balance should be 100"

    def test_deposit(self):
        # 입금 테스트
        account = BankAccount()
        account.deposit(100)
        assert account.balance == 100, "Balance should be 100 after deposit"

        with pytest.raises(ValueError):
            account.deposit(-50)

    def test_withdraw(self):
        # 출금 테스트
        account = BankAccount(200)
        account.withdraw(50)
        assert account.balance == 150, "Balance should be 150 after withdrawal"

        with pytest.raises(ValueError):
            account.withdraw(-20)

        with pytest.raises(ValueError):
            account.withdraw(300)  # 잔액 초과

 

3. 테스트 실행

위의 테스트 코드를 파일로 저장한 후, pytest를 사용하여 테스트를 실행합니다. pytest는 자동으로 TestBankAccount 클래스를 찾아 해당 테스트 메서드들을 실행하고 결과를 보여줍니다.

이런 방식으로 클래스의 각 메서드를 독립적으로 테스트하여, 객체의 다양한 상태와 상호작용을 검증할 수 있습니다. 이는 클래스의 안정성을 보장하고, 유지보수를 용이하게 하며, 예상치 못한 버그를 예방하는 데 큰 도움이 됩니다.

픽스처를 사용한 클래스 테스트

pytest의 픽스처 기능을 사용하여 테스트 코드를 재구성하면 코드의 중복을 줄이고, 테스트 각각에 필요한 객체를 쉽게 설정할 수 있습니다. 아래 예시에서는 BankAccount 인스턴스를 생성하는 데 사용되는 픽스처를 만들고, 각 테스트 함수에 필요할 때 이를 사용하는 방법을 보여줍니다.

 

픽스처를 사용한 테스트 코드

import pytest
from bank_account import BankAccount  # 클래스 정의를 별도의 모듈로 가정

# 픽스처 정의: 기본 계좌
@pytest.fixture
def account():
    return BankAccount()

# 픽스처 정의: 초기 잔액이 있는 계좌
@pytest.fixture
def account_with_balance():
    return BankAccount(100)

class TestBankAccount:
    def test_initial_balance(self, account, account_with_balance):
        # 초기 잔액 테스트
        assert account.balance == 0, "Initial balance should be zero"
        assert account_with_balance.balance == 100, "Initial balance should be 100"

    def test_deposit(self, account):
        # 입금 테스트
        account.deposit(100)
        assert account.balance == 100, "Balance should be 100 after deposit"
        # 음수 금액 입금 시도 시 예외 발생 테스트
        with pytest.raises(ValueError):
            account.deposit(-50)

    def test_withdraw(self, account_with_balance):
        # 출금 테스트
        account_with_balance.withdraw(50)
        assert account_with_balance.balance == 50, "Balance should be 50 after withdrawal"
        # 음수 금액 출금 시도 시 예외 발생 테스트
        with pytest.raises(ValueError):
            account_with_balance.withdraw(-20)
        # 잔액 초과 출금 시도 시 예외 발생 테스트
        with pytest.raises(ValueError):
            account_with_balance.withdraw(200)

 

설명

  • 픽스처 (accountaccount_with_balance): 각각 잔액이 없는 계좌와 초기 잔액이 100인 계좌를 반환하는 픽스처입니다. pytest.fixture 데코레이터는 해당 함수가 픽스처를 정의한다는 것을 pytest에 알립니다.
  • 테스트 함수: 각 테스트 함수는 필요한 픽스처를 인자로 받습니다. pytest는 이러한 테스트를 실행할 때 자동으로 적절한 픽스처 객체를 해당 테스트 함수에 제공합니다.

이렇게 픽스처를 사용하면 각 테스트가 필요로 하는 사전 조건을 명확하게 설정할 수 있으며, 코드의 반복을 줄이고, 각 테스트의 독립성을 높일 수 있습니다.

728x90
Comments