Python Unittest 대 Pytest
이 기사의 주요 목표는 Python에서 단위 테스트를 위해 가장 많이 사용되는 두 가지 프레임워크인 unittest
및 pytest
, 장단점 및 선호하는 경우에 대해 논의하는 것입니다.
Python unittest
대 Pytest
소프트웨어를 작성하는 동안 개발 프로세스 전반에 걸쳐 오류 검사 프로세스를 유지해야 합니다. 소프트웨어가 릴리스 단계에 도달하면 사용 중에 발생하는 버그가 최소화됩니다.
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)
위에 표시된 코드에는 CheckPrime
및 CalcFact
라는 두 개의 함수가 포함되어 있으며 이름에서 알 수 있듯이 소수를 확인하고 계승을 계산합니다.
계산
방법이 원활하게 작동하도록 하려면 다양한 출력을 제공하여 발생할 수 있는 오류를 확인하는 것이 필수적입니다.
어떻게 할 수 있습니까? 코드에 오류가 없는지 확인하기 위해 다양한 테스트 프레임워크를 사용하여 테스트 사례를 작성하고 그 위에 코드를 테스트하여 코드의 무결성을 확인할 수 있습니다.
많은 테스트 프레임워크가 있지만 가장 널리 사용되는 두 가지 프레임워크는 unittest
와 pytest
입니다. 아래에서 하나씩 살펴보겠습니다.
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_CheckPrime
과 test_CalcFact
가 모두 호출됩니다. 어설션은 구현에서 확인되며 예기치 않은 동작의 경우 출력에 오류가 발생합니다.
오류가 포함된 테스트 사례에서 코드 작성 방식으로 인해 CalcFact
메서드에서 무한 재귀가 발생하기 시작했으며 이제 테스트 사례 덕분에 수정할 수 있음을 추론할 수 있습니다.
오류가 발생하는 이유가 궁금하다면 초기 조건이 1보다 작은 숫자를 확인하지 않기 때문입니다.
unittest
프레임워크의 장단점
unittest
사용의 이점 중 일부는 다음과 같습니다.
- 파이썬 표준 라이브러리에 포함
- 관련 테스트 사례를 단일 테스트 스위트로 승격
- 신속한 테스트 수집
- 정확한 테스트 시간 지속
unittest
에는 다음과 같은 단점이 있습니다.
- 이해하기 어려울 수 있음
- 컬러 출력 없음
- 너무 장황할 수 있음
Pytest
프레임워크에 의한 단위 테스트
unittest
와 달리 Pytest
는 내장 모듈이 아닙니다. 별도로 다운로드해야 합니다. 그러나 Pytest 설치는 비교적 쉽습니다. 이를 위해 pip
를 사용하고 다음 명령을 실행할 수 있습니다.
pip install pytest
Pytest
를 사용하여 테스트 사례 작성
Pytest
를 사용하여 몇 가지 테스트 사례를 작성해 봅시다. 그러나 시작하기 전에 테스트 케이스 작성에서 Pytest
가 unittest
와 어떻게 다른지 살펴보겠습니다. 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
프레임워크를 사용할 때의 몇 가지 이점입니다.
-
소형 테스트 스위트
-
최소한의 상용구 코드
-
플러그인 지원
-
깔끔하고 적절한 출력 표현
또한 아래에 나열된 단점도 있습니다.
-
종종 다른 프레임워크와 호환되지 않음
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