[오늘의 헤드라인] 테스트 코드 작성기(1) — 단위 테스트 프레임워크

Gyeong Jun Paik
빅펄 (헤드라잇)
8 min readJan 4, 2021

안녕하세요. 오늘의 헤드라인 서버팀의 소프트웨어 엔지니어 Jun입니다. 오늘의 헤드라인에서 ‘python 기반 테스트 코드를 작성한 과정과 고민’을 2편으로 나누어 공유하고자 합니다.

(1) 단위 테스트 프레임워크

(2) Mock

이번 글에서는 (1) 단위 테스트 프레임워크를 알아보겠습니다.

테스트 코드를 작성하는 이유

  1. 코드의 안정성을 높일 수 있다.
좋은 제품은 테스트를 거칩니다.

제품이 시장에 나오기 전, 안정성 검증으로 퀄리티를 보장하는 것 까지가 엔지니어의 필수적인 업무입니다. 테스트 코드를 통한 CI를 진행하면서 안정성을 높이고, 프로젝트 품질을 보증할 수 있습니다.

2. 커뮤니케이션 비용을 줄일 수 있다.

훌륭한 테스트 코드는 ‘코드 사용 설명서’ 역할을 하기도 합니다. 시간이 지나며 프로젝트가 커지더라도, 테스트 코드가 있다면 어떤 의도로 코드가 작성됐는지 알 수 있습니다

3. 프로젝트 코드 품질을 유지하며 운영할 수 있다.

테스트 코드를 추가했을 때, 코드에 변경사항이 있더라도 기존에 있었던 기능들이 잘 작동하는 것을 쉽게 확인할 수 있습니다. 테스트 코드는 실제 코드의 품질을 떨어트리지 않으면서 테스트를 쉽게 할 수 있습니다.

4. 생각의 흐름을 정리하며 개발하기 좋다.

테스트 코드를 작성하며, 개발 당시 복잡했던 생각의 흐름이 정리된다는 느낌을 받았습니다. 테스트 코드를 작성하는 과정이 ‘생각을 단위로 정리’하면서 ‘글을 읽는 느낌을 주는 코드’를 짜는데 도움이 되었기 때문입니다. 이제 제게 테스트 코드는 선택이 아닌 필수가 되었습니다.

Python의 단위 테스트 프레임워크

오늘의 헤드라인의 서버팀은 프로젝트에 가장 적합한 자원을 사용하기 위해 분석하고 고민합니다. 단위테스트 프레임워크를 선택할 때,어떤 기준으로 생각했는지 정리하여 이야기 해보겠습니다.

python의 대표적인 단위 테스트 프레임워크로는 unittest와 pytest를 살펴보았습니다. 그 외에 doctest와 nose가 있지만 아래의 이유로 선택 기준에서 제외했습니다.

doctest : 주석을 이용한 구조로 테스트 코드를 작성하는데, 다양한 케이스의 테스트 코드(실패하는 테스트 코드 등)를 주석에서 관리하는 것이 의문이라는 이야기가 많아 제외했습니다.

nose: 메인테이너의 부재로 다른 테스트 프레임워크를 사용할 것을 권장하고 있어 제외했습니다.

unittest

unittest는 Java의 단위 테스트 프레임워크인 JUnit으로부터 영감을 받아 만들어진 프레임워크 입니다. 장점으로는 Python 2.7 버전 이상부터 기본적으로 내장되어 추가 설치가 필요하지 않습니다. Junit의 영향을 많이 받아 자바를 사용해 본 분들에게 친숙할수 있습니다. 개인적인 견해지만 unittest에 아쉬움이 있습니다.

Class의 의무적인 사용

unittest는 TestCase class를 사용하지 않고 테스트 코드를 작성하기 어렵습니다.

매번 정의해야하는 fixture

fixture에 관하여 간단하게 설명하겠습니다.

fixture란?

직역하면 “고정 장치”라는 뜻입니다.

테스트 코드가 실행되기 전 test의 필요한 데이터나 리소스를 load 하여 사용 가능한 형태라고 설명할 수 있습니다.

unittest는 fixture를 사용하기 위해 Class 안에 setUp과 teatDown이라는 메서드를 작성해야합니다.

setUp: 테스트 코드가 동작하기 전, fixture를 불러오는 역할을 합니다.

tearDown: 테스트 코드 동작 후, 자원을 해제하는 역할을 합니다.

매번 setUp/tearDown 메서드를 정의해야하는 부분이 불편하다고 생각합니다.

Camel case 사용

unittest의 메서드

Python의 Convention에서 메서드는 snake case를 사용합니다.

그러나 unitest는 Python의 Convension보다 먼저 만들어졌고, Java의 Junit을 기반으로 만들어졌기 Java의 컨벤션을 따른 것으로 보입니다.

pytest

pytest는 간단하고 확장 가능한 테스트를 쉽게 구축할 수 있는 프레임워크입니다.

강력하고 쉬운 fixture는 pytest의 큰 장점이라고 생각합니다.

fixture 작성 예시 입니다.

scope와 autouse는 fixture에서 자주 사용하는 옵션입니다.

scope 옵션은 fixture의 life cycle을 의미합니다. “function”이 default 값이며 그 외에 “class”, “module”, (“package” or “session”) 등이있습니다.

autouse 옵션은 테스트 코드에서 명시적으로 call 하지 않아도 사용할 수 있는 옵션입니다. db 또는 redis등의 전역 코드에 해당하는 fixture를 사용할 때 편하게 사용할 수 있습니다. default 값은 False입니다.

테스트 코드에서 fixture를 사용하는 예시입니다.

위에 테스트 코드가 여러 개라도, 함수의 파라미터를 지정하듯 간편하게 호출해서 사용할 수 있습니다.

강력한 플러그인 다수 존재

315개 이상의 pytest 플러그인이 있습니다. 테스트를 위한 코드를 작성하기 전에 플러그인을 먼저 확인해보세요 :)

경험으로는 테스트 코드가 많아질수록 실행 속도가 오래 걸려 병렬처리 작업이 필요했는데 pytest-xdist 설치와 옵션 하나로 쉽게 병렬 처리를 할 수 있었습니다.

유연한 코드 작성 가능

pytest의 테스트 코드

pytest는 Class 사용 없이 function만으로 테스트 코드를 작성할 수 있습니다. Class 형식도 지원하면서, unittest도 함께 사용이 가능합니다.

대형 오픈소스에서 사용

SQLAlchemy, Flask 등의 대형 오픈소스에서도 pytest는 사용됩니다. 대형 오픈소스를 사용하는 수 많은 사람들에게 안정성과 품질을 인정받고 사랑받는다는 의미이기도 합니다.

오늘의 헤드라인의 서버팀이 pytest를 사용하는 이유
  1. 오늘의 헤드라인의 Python 코드는 Pythonic 함을 지향합니다.
  2. 유연하고, 느슨한 결합의 구조를 지향합니다.
  3. 모호함보단 명시적인 코드를 지향합니다.
  4. 지속 가능한 빠른 개발 cycle을 만들기 위해 복잡도를 낮추는 방법을 항상 고민합니다.

서버 팀의 지향점을 생각해 볼 때, unittest보단 pytest가 더 적합하다고 판단했습니다.

테스트 코드 작성 팁

  1. 소스 코드와 테스트 코드의 구조는 단순하고 간단하게 구성합니다.
src와 tests의 구조를 통일했습니다.

소스 코드와 테스트 코드의 구조가 복잡하면 이해하기 위해 코드에 대한 추측을 하게 됩니다. 이 내용은 클린코드와 이여지니 잠시 책의 내용을 인용하겠습니다.

Clean code is simple and direct. Clean code reads like well-written prose. Clean code never obscures the designer’s intent but rather is full of crisp abstractions and straightforward lines of control.

지속 가능한 개발을 위해 코드는 명확하고 이해하기 쉽게 작성해야 합니다. 추측을 만들지 않는 명확한 코드를 작성해야 합니다.

클린 코드

자세한 내용은 위 책을 읽는 것을 추천합니다.

2. conftest.py를 사용한다.

테스트 코드를 실행하기전 conftest.py가 우선 실행됩니다. conftest안에 테스트에 필요한 세팅을 합니다. 보통 fixture 데이터를 설정하는 작업을 합니다.

3. 자주 사용하는 pytest의 플러그인

pytest-env # 테스트 환경 변수 설정에 사용하는 플러그인입니다.pytest-mock # mocking 작업을 할 때 사용하는 플러그인입니다.pytest-asyncio # coroutine 함수를 테스트 할 때 사용하는 플러그인입니다.pytest_freezegun # 특정 시간으로 시간을 멈출 수 있습니다. 
# fixture의 time 객체를 테스트 할 때 사용하는 플러그인입니다.
pytest-xdist # 테스트 코드를 병렬로 실행할 때 사용하는 플러그인입니다.

다음 포스팅은 오늘의 헤드라인의 Mock에 관한 이야기를 다루려고 합니다.

긴 글 읽어주셔서 감사합니다.

모두 코로나 조심하시고, 새해 복 많이 받으세요 :)

오늘의 헤드라인 팀에서 함께할 ML엔지니어를 찾습니다!

  • 오늘의 헤드라인은 머신러닝 및 딥러닝 기술이 핵심인 뉴스 추천 서비스입니다.
  • 오늘의 헤드라인 ML/추천 팀은 머신러닝 및 데이터 기술을 적극적으로 활용하여 서비스에 적용하고 있습니다.

함께 대한민국 국민들의 뉴스 소비 습관을 혁신하고 싶은 분 연락주세요.

오늘의 헤드라인 ML 엔지니어 채용 공고

Reference

--

--