TDD on Python with Kata - [1] Poetry + Pytest 환경 설정

sjk·2022년 10월 25일
0

TDD on Python

목록 보기
1/2
post-thumbnail

Introduction

Python에서 TDD (Test Driven Development) 환경을 구성하고, 간단한 TDD Kata를 진행하며 익숙해져 보겠습니다.

In this Series ...

  • 테스트 프레임워크로 Pytest 를 사용합니다.
  • 패키징과 의존성 관리 도구로 Poetry 를 사용합니다.
  • 다양한 환경에서의 테스트와 CI/CD를 위해 Tox 를 사용합니다.
  • 코드는 Github 저장소에서 관리됩니다.
  • CircleCI 를 통해 CI/CD 파이프라인을 관리합니다.

Poetry

pyenvpoetry 가 설치된 상태를 가정하겠습니다. 설치되지 않은 경우 다음 문서를 참고하세요.
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.tomlpoetry.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 Kata

이 글에서 TDD로 해결하고자 하는 예제는 다음 페이지에 소개되어 있습니다.
osherove.com/tdd-kata-1

테스트는 Arrange/Act/Assert (AAA) 패턴을 최대한 적용하여 작성하고자 합니다.
Bill Wake - 3A: Arrange, Act, Assert

만들고자 하는 어플리케이션은 String Calculator 입니다.
시작 전 주의사항은 다음과 같습니다.

  • 다음 Task의 내용을 미리 읽지 마세요.
  • 한번에 하나의 Task를 처리하세요. 점진적인 작업을 통해 배우는 것이 중요합니다.
  • 올바른 입력값에 대해서만 테스트하세요. 이 Kata는 잘못된 입력에 대해서까지 테스트할 필요는 없습니다.

Task 1

다음 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을 확인하였습니다.

Next Article

Task 1번의 나머지 구현사항부터 차례로 진행하며 tox를 이용해 여러 Python 인터프리터 환경에서의 테스트를 진행하도록 하겠습니다.

profile
Backend, Infra, ML Engineering

0개의 댓글

Powered by GraphCDN, the GraphQL CDN