Getting Started
- 프로젝트 폴더 생성
mkdir pytest-demo
- calculator.py
파일 생성한 후 테스트를 위한 간단한 계산기를 Class로 작성
class Calculator:
def add(a:int,b:int) -> int:
return a + b
def subtract(a:int,b:int) -> int:
return a - b
def multiply(a:int,b:int) -> int:
return a * b
def divide(a:int,b:int) -> int:
return a / b
- calculator.py
아래에 코드를 추가로 작성해 계산기를 위한 테스트 작성해보자
# --------------------------------
# Test
# --------------------------------
class TestCalculator:
@classmethod
def setup_class(cls) -> None:
cls.calculator : Calculator = Calculator()
@classmethod
def teardown_class(cls) -> None:
del cls.calculator
def test_add_two_int(self) -> None:
assert self.calculator.add(a=3,b=5) == 8
assert self.calculator.add(a=1,b=1) == 2
assert self.calculator.add(a=0,b=3) == 3
assert self.calculator.add(a=-1,b=-2) == -3
def test_substract_two_int(self) -> None:
assert self.calculator.subtract(a=3,b=3) == 0
assert self.calculator.subtract(a=3,b=2) == 1
assert self.calculator.subtract(a=3,b=-4) == 7
def test_multiply_two_int(self) -> None:
assert self.calculator.multiply(a=5,b=5) == 25
def test_divide_two_int(self) -> None:
assert self.calculator.divide(a=5,b=5) == 1.0
pytest 설치
python3 -m venv dev
source dev/bin/activate
pip install pytest
테스트 실행
pytest -v calculator.py
========================================== test session starts ==========================================
platform darwin -- Python 3.10.2, pytest-7.2.0, pluggy-1.0.0 -- /Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10
cachedir: .pytest_cache
metadata: {'Python': '3.10.2', 'Platform': 'macOS-13.2.1-arm64-arm-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'html': '3.2.0', 'Faker': '15.3.4', 'metadata': '2.0.4'}}
rootdir: /Users/jaehyolee/git/Python/pytest/pytest-demo
plugins: html-3.2.0, Faker-15.3.4, metadata-2.0.4
collected 4 items
calculator.py::TestCalculator::test_add_two_int PASSED [ 25%]
calculator.py::TestCalculator::test_substract_two_int PASSED [ 50%]
calculator.py::TestCalculator::test_multiply_two_int PASSED [ 75%]
calculator.py::TestCalculator::test_divide_two_int PASSED [100%]
=========================================== 4 passed in 0.04s ===========================================
코드와 테스트 분리하기
현재 위에서는 calculator.py 파일 안에
계산기 코드와 계산기 코드를 테스트하는 코드가 같이 존재한다.
테스트 코드와 계산기 코드를 분리해 조금 더 가독성 있게 만들어보자.
코드 분리
pytest 공식 문서 에서는 두 가지의 방법 중 하나를 추천한다.
해당 포스팅에서는 첫번째 방법인 별도의 tests
폴더를 만들어 정리해보자.
calculator
라는 상위 폴더를 생성하고 calculator.py
파일을 calculaotr
폴더로 옮기자.
mv calculator.py ./calculator
그리고 calculator 폴더에 빈 __init__.py
파일을 생성하자
__init__.py
파일은 python이 폴더를 패키지 로 처리하기 위해 생성하는 것임
pytest를 한번 실행해보자
pytest -v tests/test_calculator.py
========================================== test session starts ==========================================
platform darwin -- Python 3.10.2, pytest-7.2.0, pluggy-1.0.0 -- /Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10
cachedir: .pytest_cache
metadata: {'Python': '3.10.2', 'Platform': 'macOS-13.2.1-arm64-arm-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'html': '3.2.0', 'Faker': '15.3.4', 'metadata': '2.0.4'}}
rootdir: /Users/jaehyolee/git/Python/pytest/pytest-demo/calculator
plugins: html-3.2.0, Faker-15.3.4, metadata-2.0.4
collected 4 items
tests/test_calculator.py::TestCalculator::test_add_two_int PASSED [ 25%]
tests/test_calculator.py::TestCalculator::test_substract_two_int PASSED [ 50%]
tests/test_calculator.py::TestCalculator::test_multiply_two_int PASSED [ 75%]
tests/test_calculator.py::TestCalculator::test_divide_two_int PASSED [100%]
=========================================== 4 passed in 0.04s ===========================================
오류 없이 동작하고 있는 것을 확인할 수 있었다.
pytest 데코레이터
@pytest.mark.parametrize()
에 전달하는 인수를 자세히 봐보자.
@pytest.mark.parametrize('변수명', [(데이터1), (데이터2)])
형식으로 추가해서 매개변수화가 가능하다.
아래 코드를 봐보자
@pytest.mark.parametrize('a,b,expected',[(3,5,8)])
def test_add_two_int(self,a,b,expected) -> None:
assert self.calculator.add(a=a,b=b) == expected
위 코드로 변경 후 실행해도 위와 같이 똑같은 결과가 나온다
========================================== test session starts ==========================================
platform darwin -- Python 3.10.2, pytest-7.2.0, pluggy-1.0.0 -- /Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10
cachedir: .pytest_cache
metadata: {'Python': '3.10.2', 'Platform': 'macOS-13.2.1-arm64-arm-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'html': '3.2.0', 'Faker': '15.3.4', 'metadata': '2.0.4'}}
rootdir: /Users/jaehyolee/git/Python/pytest/pytest-demo/calculator
plugins: html-3.2.0, Faker-15.3.4, metadata-2.0.4
collected 4 items
tests/test_calculator.py::TestCalculator::test_add_two_int[3-5-8] PASSED [ 25%]
tests/test_calculator.py::TestCalculator::test_substract_two_int PASSED [ 50%]
tests/test_calculator.py::TestCalculator::test_multiply_two_int PASSED [ 75%]
tests/test_calculator.py::TestCalculator::test_divide_two_int PASSED [100%]
=========================================== 4 passed in 0.04s ===========================================
자 이제 그러면 위 데코레이터를 적용하여 코드를 리팩토링 해보자
from calculator.calculator import Calculator
import pytest
class TestCalculator:
@classmethod
def setup_class(cls) -> None:
cls.calculator : Calculator = Calculator()
@classmethod
def teardown_class(cls) -> None:
del cls.calculator
@pytest.mark.parametrize('a,b,expected',[(3,5,8)])
def test_add_two_int(self,a,b,expected) -> None:
assert self.calculator.add(a=a,b=b) == expected
@pytest.mark.parametrize('a,b,expected',[(5,5,0)])
def test_substract_two_int(self,a,b,expected) -> None:
assert self.calculator.subtract(a=a,b=b) == expected
@pytest.mark.parametrize('a,b,expected',[(5,5,25)])
def test_multiply_two_int(self,a,b,expected) -> None:
assert self.calculator.multiply(a=a,b=b) == expected
@pytest.mark.parametrize('a,b,expected',[(5,5,1.0)])
def test_divide_two_int(self,a,b,expected) -> None:
assert self.calculator.divide(a=a,b=b) == expected
다시 pytest를 실행하면 오류 없이 동작하는 것을 확인할 수 있습니다.
@pytest.mark.parametrize()
에 전달하는 인수를 자세히 알아봅시다.
먼저, 문자열로 함수에 전달될 매개변수명을 순서대로 지정해 줍니다. 여기서는 테스트 함수가 a
, b
와 expected
를 전달받기 때문에 'a,b,expected'
를 전달합니다. 두번째 인수는 튜플이 담긴 리스트입니다. 튜플은 첫 번째 인자의 매개변수 순서대로 지정해야 하며, 리스트 내의 모든 튜플을 루프로 돌아가면서 코드를 테스트합니다. 리스트에 튜플을 두개 더 추가해 테스트 항목을 늘려 봅시다.
@pytest.mark.parametrize('a,b,expected', [(3, 5, 8), (1, 1, 2), (-52, 5, -47)])
그러면 위 내용을 적용하여 테스트 항목을 더 늘려보자.
from calculator.calculator import Calculator
import pytest
class TestCalculator:
@classmethod
def setup_class(cls) -> None:
cls.calculator : Calculator = Calculator()
@classmethod
def teardown_class(cls) -> None:
del cls.calculator
@pytest.mark.parametrize('a,b,expected',[(3,5,8),(1,2,3)])
def test_add_two_int(self,a,b,expected) -> None:
assert self.calculator.add(a=a,b=b) == expected
@pytest.mark.parametrize('a,b,expected',[(5,5,0),(10,5,5)])
def test_substract_two_int(self,a,b,expected) -> None:
assert self.calculator.subtract(a=a,b=b) == expected
@pytest.mark.parametrize('a,b,expected',[(5,5,25),(4,5,20)])
def test_multiply_two_int(self,a,b,expected) -> None:
assert self.calculator.multiply(a=a,b=b) == expected
@pytest.mark.parametrize('a,b,expected',[(5,5,1.0),(4,2,2)])
def test_divide_two_int(self,a,b,expected) -> None:
assert self.calculator.divide(a=a,b=b) == expected
실행해보면 아래와 같이 테스트 항목 결과가 늘어난 것을 볼 수 있다.
========================================== test session starts ==========================================
platform darwin -- Python 3.10.2, pytest-7.2.0, pluggy-1.0.0 -- /Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10
cachedir: .pytest_cache
metadata: {'Python': '3.10.2', 'Platform': 'macOS-13.2.1-arm64-arm-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'html': '3.2.0', 'Faker': '15.3.4', 'metadata': '2.0.4'}}
rootdir: /Users/jaehyolee/git/Python/pytest/pytest-demo/calculator
plugins: html-3.2.0, Faker-15.3.4, metadata-2.0.4
collected 8 items
tests/test_calculator.py::TestCalculator::test_add_two_int[3-5-8] PASSED [ 12%]
tests/test_calculator.py::TestCalculator::test_add_two_int[1-2-3] PASSED [ 25%]
tests/test_calculator.py::TestCalculator::test_substract_two_int[5-5-0] PASSED [ 37%]
tests/test_calculator.py::TestCalculator::test_substract_two_int[10-5-5] PASSED [ 50%]
tests/test_calculator.py::TestCalculator::test_multiply_two_int[5-5-25] PASSED [ 62%]
tests/test_calculator.py::TestCalculator::test_multiply_two_int[4-5-20] PASSED [ 75%]
tests/test_calculator.py::TestCalculator::test_divide_two_int[5-5-1.0] PASSED [ 87%]
tests/test_calculator.py::TestCalculator::test_divide_two_int[4-2-2] PASSED [100%]
=========================================== 8 passed in 0.05s ===========================================
최종 파일 트리
tree .
.
└── calculator
├── __init__.py
├── calculator.py
└── tests
├── __init__.py
└── test_calculator.py