Python Unittest 대 Pytest

Salman Mehmood 2023년10월10일
Python Unittest 대 Pytest

이 기사의 주요 목표는 Python에서 단위 테스트를 위해 가장 많이 사용되는 두 가지 프레임워크인 unittest pytest, 장단점 및 선호하는 경우에 대해 논의하는 것입니다.

Python unittestPytest

소프트웨어를 작성하는 동안 개발 프로세스 전반에 걸쳐 오류 검사 프로세스를 유지해야 합니다. 소프트웨어가 릴리스 단계에 도달하면 사용 중에 발생하는 버그가 최소화됩니다.

Python에는 동작을 확인하기 위해 다양한 입력을 제공하여 작성된 코드를 테스트할 수 있는 다양한 테스트 프레임워크가 있습니다.

오류가 발생하는 경우 응용 프로그램의 초기 릴리스 이후 핫픽스와 달리 개발 단계에서 수정할 수 있습니다.

예제 코드:

class Calculate:
    def CheckPrime(self, a):
        for i in range(a):
            if a % i:
                return False
        return True

    def CalcFact(self, a):
        if a == 1:
            return a
        else:
            return a * self.fact(a - 1)

위에 표시된 코드에는 CheckPrimeCalcFact라는 두 개의 함수가 포함되어 있으며 이름에서 알 수 있듯이 소수를 확인하고 계승을 계산합니다.

계산 방법이 원활하게 작동하도록 하려면 다양한 출력을 제공하여 발생할 수 있는 오류를 확인하는 것이 필수적입니다.

어떻게 할 수 있습니까? 코드에 오류가 없는지 확인하기 위해 다양한 테스트 프레임워크를 사용하여 테스트 사례를 작성하고 그 위에 코드를 테스트하여 코드의 무결성을 확인할 수 있습니다.

많은 테스트 프레임워크가 있지만 가장 널리 사용되는 두 가지 프레임워크는 unittestpytest입니다. 아래에서 하나씩 살펴보겠습니다.

unittest 프레임워크에 의한 단위 테스트

unittest는 Python 표준 라이브러리에 포함된 단위 테스트 프레임워크입니다. 이 프레임워크는 단위 테스트를 위한 Java 프레임워크인 JUnit에서 영감을 받았습니다.

unittest의 작업을 논의하기 전에 unittest(다른 관련 프레임워크에서도 사용됨)에서 일반적으로 사용되는 용어를 아는 것이 중요합니다.

  • Test Case – 가장 작은 테스트 단위 – 일반적으로 단일
  • Test Suite – 그룹화된 테스트 사례 – 일반적으로 차례대로 실행
  • Test Runner – 테스트 케이스 및 제품군의 실행을 조정하고 처리합니다.

unittest 프레임워크를 사용하여 테스트 사례 작성

Python의 표준 라이브러리에는 이미 unittest가 포함되어 있으므로 unittest를 사용하여 단위 테스트 작성을 시작하기 위해 외부 모듈을 다운로드할 필요가 없습니다.

unittest 모듈을 가져온 후 시작할 수 있습니다. 이제 이전에 살펴본 코드에 초점을 맞추겠습니다.

예제 코드:

class Calculate:
    def CheckPrime(self, a):
        for i in range(a):
            if a % i:
                return False
        return True

    def CalcFact(self, a):
        if a == 1:
            return a
        else:
            return a * self.fact(a - 1)

unittest를 사용하여 테스트 사례를 작성하려면 특정 구문을 따라야 합니다. 즉, 테스트 클래스는 unittest.TestCase의 자식이고 해당 메서드는 test_로 시작해야 합니다.

다음 코드를 고려하십시오.

import unittest


class Calculate:
    def CheckPrime(self, a):
        for i in range(2, a):
            if a % i == 0:
                return False
        return True

    def CalcFact(self, a):
        if a == 1:
            return a
        else:
            return a * self.CalcFact(a - 1)


class TestCalc(unittest.TestCase):
    def test_CheckPrime(self):
        calc = Calculate()

        # Passing different outputs
        self.assertEqual(calc.CheckPrime(2), True)
        self.assertEqual(calc.CheckPrime(3), True)

        self.assertEqual(calc.CheckPrime(4), False)
        self.assertEqual(calc.CheckPrime(80), False)

    def test_CheckFact(self):
        calc = Calculate()

        # Passing different outputs
        self.assertEqual(calc.CalcFact(2), 2)
        self.assertEqual(calc.CalcFact(3), 6)

출력:

PS D:\Unittest> python -m unittest a.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK 

출력에서 판단하면 모든 어설션이 성공했기 때문에 모든 테스트 사례가 통과되었음을 알 수 있습니다.

이제 테스트 케이스가 실패하는 경우를 시도해 봅시다.

def test_CheckFact(self):
    calc = Calculate()

    # Passing different outputs
    self.assertEqual(calc.CalcFact(2), 2)
    self.assertEqual(calc.CalcFact(3), 6)
    # Supposed to throw an error
    self.assertEqual(calc.CalcFact(0), 0)

출력:

PS D:\Unittest> python -m unittest a.py
======================================================================
ERROR: test_CheckFact (a.TestCalc)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\Python Articles\a.py", line 34, in test_CheckFact
    self.assertEqual(calc.CalcFact(0), 0) # Supposed to throw an error
  File "D:\Python Articles\a.py", line 15, in CalcFact
    return a * self.CalcFact(a-1)
  File "D:\Python Articles\a.py", line 15, in CalcFact
    return a * self.CalcFact(a-1)
  File "D:\Python Articles\a.py", line 15, in CalcFact
    return a * self.CalcFact(a-1)
  [The previous line is repeated 974 more times]
  File "D:\Python Articles\a.py", line 12, in CalcFact
    if (a == 1):
RecursionError: maximum recursion depth exceeded in comparison

----------------------------------------------------------------------
Ran 2 tests in 0.004s
FAILED (errors=1)

코드에서 알 수 있듯이 python -m unittest <name_of_script.py>를 사용하여 스크립트를 실행합니다.

이 코드는 unittest 모듈이 특정 형식으로 제공된 스크립트 파일을 처리하기 때문에 테스트 클래스의 메서드를 호출하지 않고 작동합니다.

스크립트에 TestCalc가 포함되어 있으므로 unittest.TestCase의 하위 클래스는 Test Runner에 의해 자동으로 인스턴스화됩니다.

인스턴스화 후 클래스 내부에서 테스트 메서드를 찾아 순서대로 실행합니다. 메소드가 테스트 메소드로 간주되려면 test_로 시작해야 합니다.

테스트 메서드가 발견되면 순서대로 호출됩니다. 우리의 경우 test_CheckPrimetest_CalcFact가 모두 호출됩니다. 어설션은 구현에서 확인되며 예기치 않은 동작의 경우 출력에 오류가 발생합니다.

오류가 포함된 테스트 사례에서 코드 작성 방식으로 인해 CalcFact 메서드에서 무한 재귀가 발생하기 시작했으며 이제 테스트 사례 덕분에 수정할 수 있음을 추론할 수 있습니다.

오류가 발생하는 이유가 궁금하다면 초기 조건이 1보다 작은 숫자를 확인하지 않기 때문입니다.

unittest 프레임워크의 장단점

unittest 사용의 이점 중 일부는 다음과 같습니다.

  • 파이썬 표준 라이브러리에 포함
  • 관련 테스트 사례를 단일 테스트 스위트로 승격
  • 신속한 테스트 수집
  • 정확한 테스트 시간 지속

unittest에는 다음과 같은 단점이 있습니다.

  • 이해하기 어려울 수 있음
  • 컬러 출력 없음
  • 너무 장황할 수 있음

Pytest 프레임워크에 의한 단위 테스트

unittest와 달리 Pytest는 내장 모듈이 아닙니다. 별도로 다운로드해야 합니다. 그러나 Pytest 설치는 비교적 쉽습니다. 이를 위해 pip를 사용하고 다음 명령을 실행할 수 있습니다.

pip install pytest

Pytest를 사용하여 테스트 사례 작성

Pytest를 사용하여 몇 가지 테스트 사례를 작성해 봅시다. 그러나 시작하기 전에 테스트 케이스 작성에서 Pytestunittest와 어떻게 다른지 살펴보겠습니다. Pytest로 작성된 단위 테스트의 경우 다음을 수행해야 합니다.

  • 별도의 디렉토리를 생성하고 새로 생성된 디렉토리에 테스트할 스크립트를 배치합니다.
  • test_로 시작하거나 _test.py로 끝나는 파일에 테스트를 작성합니다. 예를 들면 test_calc.py 또는 calc_test.py입니다.

Pytest를 사용하여 테스트 케이스용으로 작성된 다음 코드를 고려하십시오.

def test_CheckPrime():
    calc = Calculate()

    # Passing different outputs
    assert calc.CheckPrime(2) == True
    assert calc.CheckPrime(3) == True

    assert calc.CheckPrime(4) == False
    assert calc.CheckPrime(80) == False


def test_CheckFact():
    calc = Calculate()

    # Passing different outputs
    assert calc.CalcFact(2) == 2
    assert calc.CalcFact(3) == 6
    # assert calc.CalcFact(0) == 0 # Supposed to throw an error

출력:

============================================================== test session starts ==============================================================
platform win32 -- Python 3.10.7, pytest-7.1.3, pluggy-1.0.0
rootdir: D:\Unittest
collected 2 items

test_a.py
[100%]

=============================================================== 2 passed in 0.04s ===============================================================

이제 실패한 테스트 사례로 다음을 수행합니다.

============================================================== test session starts ==============================================================
platform win32 -- Python 3.10.7, pytest-7.1.3, pluggy-1.0.0
rootdir: D:\Unittest
collected 2 items

test_a.py .F
[100%]

=================================================================== FAILURES ====================================================================
________________________________________________________________ test_CheckFact _________________________________________________________________

    def test_CheckFact():
        calc = Calculate()

        # Passing different outputs
        assert calc.CalcFact(2) == 2
        assert calc.CalcFact(3) == 6
>       assert calc.CalcFact(0) == 0 # Supposed to throw an error

test_a.py:50: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test_a.py:13: in CalcFact
    return a * self.CalcFact(a-1)
test_a.py:13: in CalcFact
    return a * self.CalcFact(a-1)
test_a.py:13: in CalcFact
    return a * self.CalcFact(a-1)
.
.
.
.
.
RecursionError: maximum recursion depth exceeded in comparison

test_a.py:10: RecursionError
============================================================ short test summary info ============================================================ 
FAILED test_a.py::test_CheckFact - RecursionError: maximum recursion depth exceeded in comparison
========================================================== 1 failed, 1 passed in 2.42s ========================================================== 

Pytest를 사용하여 작성된 테스트 사례는 unittest보다 약간 더 간단합니다. unittest.TestCase의 자식인 클래스를 생성하는 대신 메서드 시작 부분에 test_를 사용하여 테스트 함수를 작성할 수 있습니다.

Pytest 프레임워크의 장단점

다음은 Python에서 Pytest 프레임워크를 사용할 때의 몇 가지 이점입니다.

  • 소형 테스트 스위트

  • 최소한의 상용구 코드

  • 플러그인 지원

  • 깔끔하고 적절한 출력 표현

    또한 아래에 나열된 단점도 있습니다.

  • 종종 다른 프레임워크와 호환되지 않음

Salman Mehmood avatar Salman Mehmood avatar

Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.

LinkedIn

관련 문장 - Python Unit Test