Entry
Django 의 테스트케이스 작성에 앞서, 테스트가 필요한 이유와 목표를 정의하고 시작하자.
비행기를 예시로 보자. 공항간 이동을 위해, 이륙->비행->착륙 을 해야하고, 출발 도착 과정에서 승객을 위해 입구도 열고 닫아야 한다.
또, 비상시 구명조끼와 보트 등 안전장치의 작동여부 등 모든 기능을 각 비행 전 확인 후 이륙이 가능하다.
(아마 이륙 전 필요한 모든 비행기능을 체크하는 과정을 날개를 통해 본 적이 있을것이다.)
프로그램도 마찬가지다. 웹서비스의 경우, 작성한 비즈니스 로직의 작동여부부터 API request&response 등등.. 필요한 테스트가 정말 많다😇😇
테스트를 통해 개발자가 작성한 기능이 제대로 작동하는지 확인과 동시에 에러가 발생하는 경우, 올바른 조치가 가능하다. 종종 소프트웨어의 테스트를 논할 때, 소위 test coverage라는 용어도 자주 사용한다.
용어정의: Code coverage — Wikipedia
그래서 테스트의 목적은?
테스트의 목적 = 안정성!!
그럼 테스트를 통해 어떤 이점을 가질 수 있는지 살펴보자.
추가로 TDD에 간략히 소개하고 넘어간다.
개발자라면 TDD(Test-Driven Development) 방법론에 대해 들어봤을 것이다. 프로그램 개발 단계에서 보통
요구사항 및 스펙정의 -> 기능개발 -> 테스트 -> 배포
의 단계로 이해할 수 있지만, TDD는 기능에 대한 테스트코드를 먼저 작성 후 기능개발을 하는것이다.
TDD의 장점과 단점은 아래과 같다.
장점
- 코드 안정성
기능에서 발생할 수 있는 경우를 미리 테스트하여 개발하기 때문에 보다 robust한 코드를 작성할 수 있다. - 기능개발 및 디버깅 시간 단축
미리 테스트 케이스를 기반으로 개발을 하기 떄문에, 좀 더 빠른 개발이 가능하고, 디버깅 시 success/fail 여부에 맞는 결과를 바로 확인할 수 있다. - 유지보수 관리 용이
테스트케이스를 잘 정의해 정리하면 따로 문서 없이 코드와 주석으로 기능 설명이 가능하고, 추후 기능을 확장하거나 추가구현할 때 빠른 개발이 가능하다.
단점
- 생산성 저하
사실 이 이유가 가장 크다. 개발에는 due date가 존재하며, 현실적으로 test coverage는 100%를 달성할 수 없다.(고 생각한다)
결국, 테스트는 우리가 기능구현을 위한 최소한의 안전장치이며, 테스트가 존재하지 않는 코드는 함부로 사용하면 안된다. (라이브 서비스라면 더더욱..)
The Body
그럼 본문으로 들어가서, 테스트와 관련된 Django 모듈을 살펴보자.
Django 에서 테스트 관련모듈은 django.test에 구현되어 있고 문서에 따르면, django test 클래스는 Python standard library인 unittest 의 TestCase 를 상속받아 구성되어 있으며, 각 클래스마다 특징이 다르다.
Django 문서: Testing tools | Django documentation | Django (djangoproject.com)
TestCase [Document]
unittest Python standard library에서 제공하는 클래스이며, setUp teatDown run 등 각종 테스트 실행자에 필요한 인터페이스를 내장하고 있다.
SimpleTestCase [Document]
테스트에 필요한 여러 assertion 과 Django 설정, client 기능이 내장되어 있다.
TransactionTestCase [Document]
테스트와 ORM 사용이 용이하도록 초기 DB 재설정 가능, DB fixture, skipping 기능, 추가 assert* 기능 제공
TestCase [Document]
Django에서 제일 보편적으로 사용되는 테스트 클래스. 전체 클래스와 각 테스트(메소드)마다 두 개의 atomic() 블록으로 감싸져 있다. 특정 DB transaction 동작의 테스트 필요한 경우, TransactionTestCase 를 사용하면된다. 또한, 각 테스트 종료 시 지연가능한 DB 제약사항을 확인한다.
LiveServerTestCase [Document]
테스트 시작시, Django server를 시작하고, teardown 시 server를 종료하는 것 외, TransactionTestCase 와 동일하다. Selenium과 같은 dummy client를 활용하여 실제 유저 액션에 따른 브라우저의 결과값까지 테스트할 수 있다.
🧐
각 클래스마다 기능이 다양하게 존재하는데, 대부분의 테스트는 TestCase 클래스를 상속받아 작성해도 괜찮다. 필요한 기능에 맞게 골라서 사용하자.
…
그럼 Django 테스트를 작성해보자.
아래 코드 작성 기준은 문서와 조금의 실무경험을 바탕으로 작성했으며, 아래에서 사용이유를 간략히 정리한다.
위의 간단한 은행 시스템의 테스트케이스 코드 예제를 보자.
Account 계좌 model 이 있다는 가정으로 입금(deposit), 출금(withdraw), 이자(interest) 에 대한 테스트 코드를 작성해봤다.
여기서 중요하게 봐야할 부분은 setUpTestData 이다.
Python unittest 에서 제공하는 setUp , setUpClass와 django.test 에서 제공하는 setUpTestData 의 차이를 고려해서 작성했다.
각 패키지 메소드의 주석을 살펴보면, unittest.TestCase.setUp 의 경우 각 테스트(메소드)마다 실행되며, unittest.TestCase.setUpClass 와 django.test.TestCase.setUpTestData 의 경우, 테스트케이스 클래스마다 한번만 실행이 된다.
위 테스트는 계좌 발급은 한번만 실행되면 나머지 테스트를 진행할 수 있기 때문에 django.test.TestCase.setUpTestData 를 사용했다.
만약, 생성한 API 에 대한 테스트를 하기 전, 서버의 live 상태를 체크하기 위해 테스트 중 일어나는 모든요청 전에 health_check를 시도하는 테스트코드의 경우 unittest.TestCase.setUp 이 더 좋은 선택이 될 수도 있다.
실제 서비스를 위해선 위 코드보다 더 많은 양이 필요하다. 또한, 더 안전한 시스템을 위해 기능 개발 이후 필요할 때마다 테스트케이스를 추가해주는 것도 좋다.
No Test No Code
어떤 목적을 위해, 무슨 이유로 어느 모듈의 일부를 가져다 쓰는 건 결국 개발자 자신의 책임이지만 테스트코드 작성은 개발과정에서 필수이다🧑🔧
'Python > Django' 카테고리의 다른 글
[Django] select_related & prefetch_related (0) | 2022.04.24 |
---|