brunch
매거진 Django doc

Writing and running tests

by 장영석

Writing tests

Django 는 unit test 는 Python 의 표준 라이브러리 모듈(unittest)를 사용합니다. 이 모듈은 클래스 기반 접근 방식을 사용하여 테스트를 정의합니다.

아래 예제는 unittest.TestCase의 서브클래스인 django.test.TestCase 의 서브클래스를 사용한 예제입니다. 각 테스트는 트랜잭션 내에서 독립적으로 실행하여 격리를 제공합니다.

스크린샷 2020-01-15 오전 11.03.06.png

테스트를 실행할 때, 테스트 유틸리티의 기본적인 행동은 test 로 시작하는 모든 파일에서 모든 테스트 케이스(unittest.TestCase 의 subclass)를 찾은 후, 자동으로 test suite를 빌드하고 실행하는 것 입니다.


모델을 만들거나 조회하는 것이 데이터베이스에 의존적인 테스트인 경우 unittest.TestCase 보다 django.test.TestCase 를 사용하는 것이 좋습니다. unittest.TestCase 를 사용할 경우 트랜잭션마다 데이터베이스를 비우는 작업을 하지 않아 성능은 좋아질 수 있습니다. 하지만 단위 테스트에서는 성공하나 테스트 스위트에서는 실패할 가능성이 존재합니다.

Running tests

작성된 테스트는 프로젝트의 manage.py 유틸리티의 test 명령으로 실행할 수 있습니다.

스크린샷 2020-01-15 오전 11.04.24.png

테스트 탐색은 unittest 모듈의 built-in test unittest 기반으로 합니다. 기본적으로 현재 작업 폴더 하위의 "test*.py" 형식의 파일 이름을 찾습니다.


./manage.py test 뒤에 파이썬 패키지 또는 TestCase subclass 또는 안의 메서드 이름를 작성하여 특정 단위의 테스트들 실행이 가능합니다.

스크린샷 2020-01-15 오전 11.04.48.png


아래처럼 특정 폴더 경로를 사용할 수도 있습니다.

스크린샷 2020-01-15 오전 11.05.04.png


-p (또는 —pattern) 옵션을 사용하여 파일 이름 패턴을 커스텀 할 수 있습니다.

스크린샷 2020-01-15 오전 11.05.17.png


테스트 실행 중에 Ctrl-C 를 입력하면 gracefully 종료됩니다. 종료되는 동안 실패 케이스와 에러 케이스 등등이 출력되고 평소같이 데이터베이스도 파괴됩니다. Ctrl-C 입력은 옵션에 —failfast 를 잊었을 때 사용하면 좋습니다.


Ctrl-C 입력을 통해 gracefull exit 이 진행중인 상태에서 한 번 더 Ctrl-C 를 입력하면 기대하는 것처럼 gracefully 종료되지 않습니다. 테스트 결과에 대한 리포트도 없고 데이터베이스도 파괴되지 않습니다.


Test with warnings enabledpython -Wa manage.py test 를 사용하여 deprecation warnings 또는 코드 개선점 등을 확인하는 것도 좋은 방법입니다.


The test database

테스트(모델 테스트)에는 production 이 아닌 데이터베이스가 필요합니다. 테스트를 위해 분리된 비어있는 데이터베이스가 생성됩니다.


모든 테스트가 실행되면 통과 실패 여부와 관계없이 테스트 데이터베이스는 제거됩니다.

test —keepdb 옵션을 사용하여 테스트 데이터베이스가 제거되는 것을 막을 수 있습니다. 이를 사용하면 실행 간에 테스트 데이터베이스를 보존할 수 있습니다. 데이터베이스가 없다면, 최초에는 생성될 것입니다. 최신 상태를 유지하기 위해 migration 도 적용됩니다.


이전에 설명한 것과 같이 테스트 실행을 강제로 중단하면, 테스트 데이터베이스가 제거되지 않습니다. 다음 실행 시 해당 데이터베이스를 다시 사용할 것 인지 제거할 것인지 묻게 됩니다. test —noinput 옵션을 사용하면 질문 없이 자동으로 데이터베이스를 제거합니다.


디폴트 테스트 데이터베이스 이름들은 DATABASES 의 각 NAME 값에 test_ 접두어를 추가하여 생성됩니다. SQLite 를 사용하고 있다면, 테스트는 기본적으로 in-memory 데이터베이스를 사용합니다. DATABASES 의 TEST 딕셔너리는 테스트 데이터베이스 설정에 관련된 몇가지 세팅들을 제공합니다. 예를 들어, DATABASES 의 TEST 딕셔너리에 NAME 을 지정하여 테스트 데이터베이스 이름을 변경할 수 있습니다.


테스트 데이터베이스에 세밀한 조정을 위해 CHARSET TEST 옵션을 사용할 수 있습니다. MySQL 의 경우 COLLATION 옵션을 사용할 수 있습니다. TEST 딕셔너리의 자세한 설명은 settings documentation 을 참고 바랍니다.


SQLites 의 in-memory 데이터베이스를 사용하면 shared cache 가 활성화 됩니다. 스레드 간에 데이터베이스를 공유하는 테스트를 작성할 수 있습니다.


Rollback emulation

마이그레이션에서 로드된 초기 데이터는 TestCase 테스트에서만 사용가능하고 TransactionTestCase 에서는 사용할 수 없으며, 트랜잭션을 지원하는 백엔드에서만 가능합니다.(트랜잭션을 지원하지 않는 대표적인 예 MyISAM) LiveServerTestCase 나 StaticLiveServerTestCase 같은 TransactionTestCase 에 의존하는 테스트도 마찬가지입니다.


Django 는 TestCase 또는 TransactionTestCase 의 본문에 serilized_rollback 옵션을 True 로 설정하여 테스트 케이스마다 데이터를 다시 로드 할 수 있습니다. 하지만 테스트 속도가 약 3배 느려질 수도 있습니다.


serialized 데이터가 두 번 로드 되는 것을 막기 위해, serialized_rollback=True 설정은 테스트 데이터베이스가 flush 될 때 post_migrate 시그널을 비활성화 합니다.


Other test conditions


설정 파일의 DEBUG 세팅과 관련 없이 Django 테스트는 DEBUG=False 로 실행됩니다. 이는 테스트 환경을production 결과와 일치시키기 위함입니다.


Understanding the test output


테스트가 실행될 때, 테스트 러너가 준비하는 과정을 메시지로 확인할 수 있습니다. verbosity 옵션을 사용하여 출력 메시지에 대한 미세한 조정이 가능합니다.

스크린샷 2020-01-15 오전 11.07.05.png


테스트 데이터베이스 생성이 완료되고 모든 테스트가 정상적으로 진행되었다면 아래과 같은 메시지가 출력됩니다.

스크린샷 2020-01-15 오전 11.07.17.png


테스트가 실패하게 되면 실패한 이유가 자세히 출력됩니다.

스크린샷 2020-01-15 오전 11.07.36.png


테스트 러너는 오류가 발생하면 리턴 코드가 1 성공하면 0 입니다. 쉘 스크립트 등에서 해당 테스트를 동작시키고 결과를 판단할 때 사용하면 좋습니다.


Speeding up the tests


Running tests in parallel

모든 테스트들이 독립성이 보장된다면 멀티 코어 하드웨어를 활용해 속도를 높일 수 있습니다. test —parallel 을 참고바랍니다.


Password hashing

default password hasher 는 느리게 설계되어있습니다. 테스트에서 많은 유저의 인증이 필요한 경우 커스텀 설정 파일을 만들고 PASSWORD_HASHERS 에서 빠른 해싱 알고리즘을 추가합니다.

스크린샷 2020-01-15 오전 11.08.12.png

fixture 에서 사용되는 알고리즘이 있다면 PASSWORD_HASHERS 에 추가 바랍니다.


Preserving the test database

test —keepdb 옵션은 테스트 실행 간에 테스트 데이터베이스를 유지합니다. 생성과 삭제 등을 스킵하여 테스트 실행 시간을 감소시킬 수 있습니다.

keyword
매거진의 이전글Testing in Django