저는 pytest 를 coding중에 Unit Test 를 위해서 사용합니다.
저의 Python 환경은 3.4이상이며 저는 현재 3.6.9를 사용합니다. 3.4이하에서는 에러가 발생할 수 있습니다.
반드시 pytest를 사용해야 하는 것은 아닙니다.
다른 unit test lib들이 많이 있습니다만 저는 pytest를 주로 사용하기 때문에 앞으로 pytest를 사용하면서 코드 설명해나갈 예정입니다.
오늘의 핵심은 if __name__ == ‘__main__’: 대신 pytest를 사용하는 것을 설명합니다.
pytest 설치
pip3 install pytest
pip install pytest
환경에 맞게 사용하시면 됩니다.
test_ 또는 _test 그리고 실행
pytest에 의해서 실행될 파일과 함수는 test_ 또는 _test를 앞이나 뒤에 붙여야 합니다.
그래서 test_가 붙은 파일의 폴더에서 pytest 를 실행하면 pytest는 test_가 붙은 파일과 파일내에 test_가 붙은 함수들을 실행합니다.
다음은 Chapter 1에서 설명한 Thread Safe 싱글톤 패턴 코드를 pytest로 실행한 결과입니다.

$ pytest 만 실행했습니다.
pytest code
Chapter 1에서 설명한 Thread Safe 싱글톤 패턴 코드를 pytest를 이용하여 수정하였습니다.
Singleton.py
이전과 같습니다.
from threading import Lock
class Singleton(type):
_instance = None
_lock = Lock()
def __call__(cls, *args, **kwargs):
with cls._lock:
if not cls._instance:
cls._instance = super().__call__(*args, **kwargs)
return cls._instance
SingletonClass.py
다 같지만 if __name__ == ‘__main__’: 이 없어졌습니다.
이 코드를 pytest code로 옮기는 것이 핵심입니다.
from Singleton import Singleton
class SingletonClass(metaclass=Singleton):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name;
tests/test_SingletonClass.py
def run은 test_가 없기 때문에 pytest에 의해서 실행되지 않습니다.
test_Singleton은 pytest에 의해서 실행됩니다.
import sys
sys.path.append('..')
from SingletonClass import SingletonClass
from threading import Thread
def run(name, result):
singleton = SingletonClass(name)
result[0] = str(singleton)
def test_Singleton():
resultA = ['A']
resultB = ['B']
processa = Thread(target=run, args=('ACLASS', resultA))
processb = Thread(target=run, args=('BCLASS', resultB))
processa.start()
processb.start()
assert(resultA[0] == resultB[0])
위 코드에서 추가 설명이 필요한 코드는 다음과 같습니다.
- sys.path.append(‘..’)는 상위 폴더 모듈을 참조하고자 path에 해당 경로를 추가합니다.
- resultA와 resultB가 List인 이유는 결과값을 thread process function으로부터 받아오고자 하면서 List는 Shallow copy가 되기 때문입니다. 즉, run안에서 resultA가 새로 생성되지 않고 test_Singleton내에 만들어진 resultA 객체가 그대로 전달되어 run 함수 안에서 list 내부의 값을 수정하는 것은 같이 수정됩니다.
- assert (resultA[0] == resultB[0]) 은 True이면 pass, False이면 Fail을 표시합니다.
결과
결과는 test_SingletonClass.py에서 1개의 test case가 발견되어 pass하였습니다.

python에서 Unit Test 에 대한 개인적 의견
저는 python은 주로 command base의 program 특히 backend Program용으로 많이 사용합니다.
그렇다 보니 UI가 있는 Front End보다는 직접 손으로 해볼만한 UI는 없고 command로만 이루어집니다.
이전까지만 해도 if __name__ == “__main__”: 을 사용했습니다.
항상 파일 아래에 추가해서 사용했는데요.
다음의 문제들이 pytest를 사용함으로써 해결이 되었습니다.
즉, 반대로 pytest를 사용했을때에는 장점이 되겠죠?
- 모든 파일을 일일히 실행해야 한다.
- 일관된 test case를 만들지 않게 된다.
- Test Code Coverage가 낮다.
- import가 복잡해진다.
pytest로 대체했을때 단점은 딱 하나입니다.
- 코딩할 때 시간이 더 걸린다.
하지만 앞으로 Rest API를 release하는데 있어 매우 중요한 포인트가 될것 같습니다.