Python에서 TDD (Test Driven Development) 환경을 구성하고, 간단한 TDD Kata를 진행하며 익숙해져 보겠습니다.
pyenv와 poetry 가 설치된 상태를 가정하겠습니다. 설치되지 않은 경우 다음 문서를 참고하세요.
wikidocs - pyenv
poetry official
poetry를 이용해서 새로운 프로젝트를 시작하겠습니다.
-> % poetry new tdd-kata
Created package tdd_kata in tdd-kata
-> % cd tdd-kata && tree
.
├── README.md
├── pyproject.toml
├── tdd_kata
│ └── __init__.py
└── tests
└── __init__.py
2 directories, 5 files
poetry와 pyproject.toml 파일을 통해 프로젝트를 관리합니다. author와 python version을 제외한 파일의 내용은 다음과 같을 것입니다.
[tool.poetry]
name = "tdd-kata"
version = "0.1.0"
description = ""
authors = ["sjk <18274655+sejkimm@users.noreply.github.com>"]
readme = "README.md"
packages = [{include = "tdd_kata"}]
[tool.poetry.dependencies]
python = "^3.9"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
poetry를 이용해 Python 패키지 저장소 의 패키지를 설치할 수 있습니다.
--group dev
인자값을 추가하였기 때문에 pytest 패키지는 dev dependency에 추가될 것입니다.
-> % poetry add --group dev pytest
Using version ^7.1.3 for pytest
Updating dependencies
Resolving dependencies... (0.1s)
Writing lock file
Package operations: 8 installs, 0 updates, 0 removals
• Installing pyparsing (3.0.9)
• Installing attrs (22.1.0)
• Installing iniconfig (1.1.1)
• Installing packaging (21.3)
• Installing pluggy (1.0.0)
• Installing py (1.11.0)
• Installing tomli (2.0.1)
• Installing pytest (7.1.3)
다시 한번 pyproject.toml 파일의 내용을 확인해 보겠습니다.
[tool.poetry]
name = "tdd-kata"
version = "0.1.0"
description = ""
authors = ["sjk <18274655+sejkimm@users.noreply.github.com>"]
readme = "README.md"
packages = [{include = "tdd_kata"}]
[tool.poetry.dependencies]
python = "^3.9"
[tool.poetry.group.dev.dependencies]
pytest = "^7.1.3"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.group.dev.dependencies] 에 pytest가 추가된 것을 확인할 수 있습니다.
pyproject.toml 과 poetry.lock 파일이 있으면, 새로운 Directory에서 다음 명령어로 동일한 패키지 의존성 환경을 만들 수 있습니다.
poetry install
위 명령어는 pyproject.toml 파일을 파싱하여 의존성 환경을 구성합니다.
poetry.lock 파일이 존재하면 해당 파일에 작성된 패키지와 동일한 버전을 설치하고, poetry.lock 파일이 없다면 최신 버전으로 설치한 뒤 poetry.lock 파일을 생성합니다.
환경을 확인하기 위해 테스트 파일을 생성하고 간단한 테스트를 작성해 보겠습니다.
# /tests/test_simple.py
def test_add():
assert 1 + 2 == 3
프로젝트의 root 디렉토리에서 다음 명령어를 실행하여 poetry 환경에서 pytest 명령을 실행하고 결과를 확인할 수 있습니다.
-> % poetry run pytest
=============================== test session starts ================================
platform darwin -- Python 3.9.13, pytest-7.1.3, pluggy-1.0.0
rootdir: /Users/sejkimm/dev/project/tdd-kata
collected 1 item
tests/test_simple.py . [100%]
================================ 1 passed in 0.01s =================================
이 글에서 TDD로 해결하고자 하는 예제는 다음 페이지에 소개되어 있습니다.
osherove.com/tdd-kata-1
테스트는 Arrange/Act/Assert (AAA) 패턴을 최대한 적용하여 작성하고자 합니다.
Bill Wake - 3A: Arrange, Act, Assert
만들고자 하는 어플리케이션은 String Calculator 입니다.
시작 전 주의사항은 다음과 같습니다.
다음 Method를 가진 문자열 계산기를 만들 것입니다.
———————————————
int Add(string numbers)
———————————————
Add() Method는 comma(,)들로 나누어진 최대 두 개의 숫자 문자열을 입력받아 그들의 합을 반환합니다.
입력 예시는 "", "1", "1,2" 입니다. (빈 문자열에 대해서는 0을 반환합니다.)
힌트:
테스트 대상이 되는 빈 클래스 파일을 생성하고, 첫 번째 Task를 시작하겠습니다.
# /tdd-kata/string_calculator.py
class StringCalculator(object):
def __init__(self) -> None:
pass
def Add(self):
pass
테스트 파일을 생성하고 첫 번째 테스트를 작성합니다.
# /tests/test_string_calculator.py
from tdd_kata.string_calculator import StringCalculator
def test_add_empty_string_expect_0():
string_calculator = StringCalculator()
input_string = ""
result = string_calculator.Add(input_string)
assert result == 0
테스트를 실행합니다.
-> % poetry run pytest
=============================== test session starts ================================
platform darwin -- Python 3.9.13, pytest-7.1.3, pluggy-1.0.0
rootdir: /Users/sejkimm/dev/project/tdd-kata
collected 1 item
tests/test_string_calculator.py F [100%]
===================================== FAILURES =====================================
__________________________ test_add_empty_string_expect_0 __________________________
def test_add_empty_string_expect_0():
input_string = ""
result = StringCalculator.Add(input_string)
> assert result == 0
E assert None == 0
tests/test_string_calculator.py:21: AssertionError
============================= short test summary info ==============================
FAILED tests/test_string_calculator.py::test_add_empty_string_expect_0 - assert N...
================================ 1 failed in 0.02s =================================
RED (failed)를 마주치는데 성공했습니다.
Add() 함수의 로직을 수정하여 RED를 GREEN (pass)로 변경해 보겠습니다.
# /tdd-kata/string_calculator.py
class StringCalculator(object):
def __init__(self) -> None:
pass
def Add(self, numbers: str):
try:
result = int(numbers)
except ValueError:
result = 0
return result
-> % poetry run pytest
=============================== test session starts ================================
platform darwin -- Python 3.9.13, pytest-7.1.3, pluggy-1.0.0
rootdir: /Users/sejkimm/dev/project/tdd-kata
collected 1 item
tests/test_string_calculator.py . [100%]
================================ 1 passed in 0.01s =================================
첫 번째 테스트에 대한 GREEN을 확인하였습니다.
Task 1번의 나머지 구현사항부터 차례로 진행하며 tox를 이용해 여러 Python 인터프리터 환경에서의 테스트를 진행하도록 하겠습니다.