유닛 테스트¶
장고는 코드베이스의 “테스트” 디렉토리에 자체 테스트 스위트가 있습니다. 우리의 정책은 모든 시험을 항상 통과하도록 하는 것입니다.
테스트 제품군에 대한 모든 기여에 감사드립니다!
Django 테스트는 모두 Django와 함께 제공되는 테스트 애플리케이션을 위한 테스트 인프라를 사용합니다. 새로운 테스트 작성 방법에 대한 설명은 :doc:’/topics/testing/개요’를 참조하십시오.
장치 테스트를 실행¶
빠른시작¶
우선 장고를 깃허브 <https://github.com/django/django/fork>에서 포크하세요
둘째, 가상 환경을 생성하고 활성화합니다. 자세한 내용은 :doc:’기여 튜토리얼’을 참조하십시오.
그런 다음 포크를 복제하고 몇 가지 요구 사항을 설치한 다음 테스트를 실행하십시오.
$ git clone https://github.com/YourGitHubName/django.git django-repo
$ cd django-repo/tests
$ python -m pip install -e ..
$ python -m pip install -r requirements/py3.txt
$ ./runtests.py
...\> git clone https://github.com/YourGitHubName/django.git django-repo
...\> cd django-repo\tests
...\> py -m pip install -e ..
...\> py -m pip install -r requirements\py3.txt
...\> runtests.py
요구 사항을 설치하려면 컴퓨터에 설치되지 않은 일부 운영 체제 패키지가 필요할 수 있습니다. 일반적으로 오류 메시지의 마지막 줄에 대해 웹 검색을 수행하여 설치할 패키지를 확인할 수 있습니다. 필요한 경우 검색 쿼리에 운영 체제를 추가해 보십시오.
요구 사항을 설치하는 데 문제가 있으면 해당 단계를 건너뛸 수 있습니다. 선택적 테스트 종속성 설치에 대한 자세한 내용은:ref:’running-unit-tests-dependencies’를 참조하십시오. 선택적 종속성이 설치되어 있지 않으면 종속성이 필요한 테스트는 건너뜁니다.
테스트를 실행하려면 사용할 데이터베이스를 정의하는 Django 설정 모듈이 필요합니다. 시작할 수 있도록 Django는 SQLite 데이터베이스를 사용하는 샘플 설정 모듈을 제공하고 사용합니다. 다른 설정 모듈을 사용하여 다른 데이터베이스로 테스트를 실행하는 방법에 대한 자세한 내용은:ref:’running-unit-tests-settings’를 참조하십시오.
문제가 있나요? 몇 가지 일반적인 문제는:ref:’문제 해결 장치 테스트’를 참조하십시오.
“tox”를 사용하여 테스트를 실행합니다.¶
“Tox <https://tox.wiki/>_는 서로 다른 가상 환경에서 테스트를 실행하기 위한 도구입니다. Django에는 빌드 서버가 풀 요청에 대해 수행하는 일부 검사를 자동화하는 기본 “tox.ini”가 포함되어 있습니다. 장치 테스트 및 기타 검사(예: :ref:’import sorting’, :ref:’documentation spelling checker’ 및 :ref:’code formating’)를 실행하려면 Django 원본 트리의 임의의 위치에서 “tox” 명령을 설치하고 실행합니다.
$ python -m pip install tox
$ tox
...\> py -m pip install tox
...\> tox
기본적으로 “tox”는 SQLite에 대한 번들 테스트 설정 파일, “black”, “flake8”, “isort” 및 문서 철자 검사기와 함께 테스트 제품군을 실행합니다. 이 설명서의 다른 부분에 언급된 시스템 종속성 외에도 명령어 “python3”은 사용자의 경로에 있어야 하며 적절한 버전의 Python에 연결되어 있어야 합니다. 기본 환경 목록은 다음과 같습니다.
$ tox -l
py3
black
flake8>=3.7.0
docs
isort>=5.1.0
...\> tox -l
py3
black
flake8>=3.7.0
docs
isort>=5.1.0
다른 Python 버전 및 데이터베이스 백엔드를 테스트합니다.¶
기본 환경 외에도 “tox”는 다른 버전의 Python 및 다른 데이터베이스 백엔드에 대한 단위 테스트 실행을 지원합니다. 그러나 Django의 테스트 스위트는 SQLite 이외의 데이터베이스 백엔드에 대한 설정 파일을 번들로 제공하지 않으므로 :ref:”사용자 고유의 테스트 설정”을 만들고 제공해야 합니다. 예를 들어 Postgre를 사용하여 Python 3.9에서 테스트를 실행하려면 다음과 같이 하십시오.SQL: 입니다.
$ tox -e py39-postgres -- --settings=my_postgres_settings
...\> tox -e py39-postgres -- --settings=my_postgres_settings
이 명령은 Python 3.9 가상 환경을 설정하고 Django의 테스트 제품군 종속성(Postgre용 종속성 포함)을 설치합니다.SQL), 그리고 “테스트 실행”을 호출합니다.py는 제공된 인수(이 경우 ‘’–settings=my_postgres_settings’’)를 사용합니다.
이 문서의 나머지 부분에서는 “tox” 없이 테스트를 실행하는 명령을 보여 주지만 “runtests.py”에 전달되는 모든 옵션은 위와 같이 인수 목록에 “`–“를 붙임으로써 “tox”에 전달될 수 있습니다.
설정된 경우 Tox는 DJANGO_SETTINGS_MODULE
환경 변수도 존중합니다. 예를 들어, 다음은 위의 명령과 동일합니다.
$ DJANGO_SETTINGS_MODULE=my_postgres_settings tox -e py39-postgres
Windows 사용자는 다음을 사용해야 합니다.
...\> set DJANGO_SETTINGS_MODULE=my_postgres_settings
...\> tox -e py39-postgres
JavaScript 테스트를 실행합니다.¶
Django에는 특정 기여 앱의 기능을 위한 :ref:`JavaScript 세트가 포함되어 있습니다. JavaScript 테스트는 “Node.js”를 설치해야 하고 대부분의 패치에 필요하지 않기 때문에 기본적으로 ``tox``를 사용하여 실행되지 않습니다. “tox”를 사용하여 JavaScript 테스트를 실행하려면 다음과 같이 하십시오.
$ tox -e javascript
...\> tox -e javascript
이 명령은 테스트 요구 사항이 최신인지 확인하기 위해 “npm install”을 실행한 다음 “npm test”를 실행합니다.
‘’장고-도커박스’’를 사용하여 테스트를 실행합니다.¶
“django-docker-box”_를 사용하면 지원되는 모든 데이터베이스 및 python 버전에서 Django의 테스트 제품군을 실행할 수 있습니다. 설치 및 사용 지침은 ‘django-docker-box’_ 프로젝트 페이지를 참조하십시오.
다른 ‘’설정’’ 모듈을 사용합니다.¶
포함된 설정 모듈(‘’tests/test_sqlite.py’’)을 사용하여 SQLite를 사용하여 테스트 제품군을 실행할 수 있습니다. 다른 데이터베이스를 사용하여 테스트를 실행하려면 자체 설정 파일을 정의해야 합니다. ‘’기여’’와 같은 몇 가지 시험들이 있습니다.postgres²는 특정 데이터베이스 백엔드에 한정되며 다른 백엔드로 실행할 경우 건너뜁니다. 일부 테스트는 특정 데이터베이스 백엔드에서 건너뛰거나 실패할 것으로 예상됩니다(각 백엔드의 “DatabaseFeatures.django_test_skips” 및 “DatabaseFeatures.django_testpected_fails” 참조).
다른 설정으로 테스트를 실행하려면 모듈이 :envvar:’PYthonPATH’에 있는지 확인하고 ‘–settings’로 모듈을 통과시킵니다.
: 설정은 다음과 같습니다.모든 테스트 설정 모듈의 “DATABASES” 설정은 두 개의 데이터베이스를 정의해야 합니다.
- ‘’’기본’’’입니다. 이 데이터베이스는 기본 테스트에 사용할 백엔드를 사용해야 합니다.
- 별칭이 ‘’기타’’인 데이터베이스입니다. “기타” 데이터베이스는 쿼리를 다른 데이터베이스로 전송할 수 있는지 테스트하는 데 사용됩니다. 이 데이터베이스는 “default”와 동일한 백엔드를 사용해야 하며 이름이 달라야 합니다.
SQLite가 아닌 백엔드를 사용하는 경우 각 데이터베이스에 대한 기타 세부 정보를 제공해야 합니다.
- : 설정은 다음과 같습니다.’USER’ 옵션은 데이터베이스에 대한 기존 사용자 계정을 지정해야 합니다. 해당 사용자는 테스트 데이터베이스를 만들려면 “CREATE DATABASE”를 실행할 수 있는 권한이 필요합니다.
- :설정:’입니다.PASSWORD’ 옵션은 :setting에 대한 암호를 제공해야 합니다.지정된 ‘USER’입니다.
테스트 데이터베이스는 “test_” 값을 :setting 값에 “test_”로 추가하여 이름을 가져옵니다.:setting에 정의된 데이터베이스에 대한 “NAME” 설정은 다음과 같습니다.’데이터베이스’입니다. 이러한 테스트 데이터베이스는 테스트가 완료되면 삭제됩니다.
또한 데이터베이스에서 UTF-8을 기본 문자 집합으로 사용해야 합니다. 데이터베이스 서버가 UTF-8을 기본 문자 집합으로 사용하지 않는 경우, 해당 데이터베이스의 테스트 설정 사전에 :setting:`CHARSET 값을 포함해야 합니다.
일부 테스트만 실행합니다.¶
Django의 전체 테스트 제품군을 실행하는 데 시간이 오래 걸리며, 예를 들어 모든 테스트를 Django에 추가했을 때 다른 모든 테스트를 실행하지 않고 빠르게 실행하고자 하는 경우 모든 테스트를 실행하는 것은 중복될 수 있습니다. 명령줄에서 테스트 모듈의 이름을 ``runtests.py``에 추가하여 단위 테스트의 하위 집합을 실행할 수 있습니다.
예를 들어 일반 관계 및 국제화에 대해서만 테스트를 실행하려면 다음을 입력합니다.
$ ./runtests.py --settings=path.to.settings generic_relations i18n
...\> runtests.py --settings=path.to.settings generic_relations i18n
개별 시험의 이름은 어떻게 알 수 있나요? “tests/’를 찾습니다. 각 디렉토리 이름은 테스트 이름입니다.
특정 테스트 클래스만 실행하려면 개별 테스트 클래스에 대한 경로 목록을 지정할 수 있습니다. 예를 들어 ‘’i18n’’’ 모듈의 ‘’번역’을 실행하려면 타입:
$ ./runtests.py --settings=path.to.settings i18n.tests.TranslationTests
...\> runtests.py --settings=path.to.settings i18n.tests.TranslationTests
또한 다음과 같은 개별 검사 방법을 지정할 수 있습니다.
$ ./runtests.py --settings=path.to.settings i18n.tests.TranslationTests.test_lazy_objects
...\> runtests.py --settings=path.to.settings i18n.tests.TranslationTests.test_lazy_objects
“–start-at’ 옵션을 사용하여 지정된 최상위 모듈에서 테스트를 시작할 수 있습니다. 예를 들어 다음과 같습니다.
$ ./runtests.py --start-at=wsgi
...\> runtests.py --start-at=wsgi
또한 ‘’–start-after’’’ 옵션을 사용하여 지정된 최상위 모듈 이후에 테스트를 실행할 수도 있습니다. 예를 들어 다음과 같습니다.
$ ./runtests.py --start-after=wsgi
...\> runtests.py --start-after=wsgi
‘’’–reverse’’’ 옵션은 ‘’’–start-at’’’ 또는 ‘’’–start-after’’’ 옵션에는 영향을 미치지 않습니다. 또한 이러한 옵션은 테스트 레이블과 함께 사용할 수 없습니다.
Selenium 테스트를 실행합니다.¶
일부 테스트에는 Selenium과 웹 브라우저가 필요합니다. 이러한 테스트를 실행하려면 selenium 패키지를 설치하고 ‘’–selenium=filename’’ 옵션을 사용하여 테스트를 실행해야 합니다. 예를 들어 Firefox 및 Google Chrome이 설치된 경우 다음을 수행합니다.
$ ./runtests.py --selenium=firefox,chrome
...\> runtests.py --selenium=firefox,chrome
셀레늄을 보세요.webdriver’_package에서 사용할 수 있는 브라우저 목록입니다.
‘’–selenium’’을 지정하면 ‘’–selenium’’이 자동으로 셀레늄이 필요한 테스트만 실행되도록 설정됩니다.
일부 브라우저(예: Chrome 또는 Firefox)는 헤드리스 테스트를 지원하므로 더 빠르고 안정적입니다. 이 모드를 활성화하려면 “–headless” 옵션을 추가합니다.
모든 테스트를 실행합니다.¶
전체 테스트 제품군을 실행하려면 다음과 같은 여러 종속성을 설치해야 합니다.
- aiosmtpd
- argon2-cffi 19.1.0+
- asgiref 3.4.1+ (required)
- bcrypt
- colorama
- docutils
- geoip2
- jinja2 2.7+
- numpy
- Pillow 6.2.0+
- PyYAML
- pytz (required)
- pywatchman
- redis
- setuptools
- memcached, a :ref:’지원되는 Python 바인딩’.
- gettext (gettext on Windows)
- selenium
- sqlparse 0.2.2+ (required)
- tblib 1.5.0+
Django 원본 트리의 “tests/requirements” 디렉토리에 있는 “pip requirements files”_에서 이러한 종속성을 찾아 다음과 같이 설치할 수 있습니다.
$ python -m pip install -r tests/requirements/py3.txt
...\> py -m pip install -r tests\requirements\py3.txt
설치 중 오류가 발생했다면, 시스템이 하나 이상의 Python 패키지에 대한 의존성을 해결하지 못한 것일 수 있습니다. 설치에 실패한 패키지의 문서를 참조하거나 오류 메시지를 웹에서 검색하십시오.
“oracle”을 사용하여 원하는 데이터베이스 어댑터를 설치할 수도 있습니다.txt,, ‘’’예’’’입니다.txt, 또는 ‘’postgres’’입니다.txt``입니다.
memcached 또는 Redis 캐시 백엔드를 테스트하려면 :setting도 정의해야 합니다.memcached 또는 Redis 인스턴스를 각각 가리키는 “CACHES” 설정입니다.
GeoDjango 테스트를 실행하려면 :doc:’공간 데이터베이스를 설정하고 Geospacial Libraries를 설치해야 합니다.
이러한 각 종속성은 선택 사항입니다. 누락된 테스트가 있으면 관련 테스트를 건너뜁니다.
일부 자동 재로드 테스트를 실행하려면 Watchman 서비스를 설치해야 합니다.
코드 적용 범위¶
기여자는 추가 테스트가 필요한 영역을 식별하기 위해 테스트 제품군에 대한 적용 범위를 실행할 것을 권장합니다. 커버리지 도구 설치 및 사용은 :ref:’테스트 코드 커버리지’에 설명되어 있습니다.
적용 범위는 정확한 통계를 얻기 위해 단일 프로세스로 실행되어야 합니다. 표준 테스트 설정을 사용하여 Django 테스트 제품군에 대한 적용 범위를 실행하려면 다음과 같이 하십시오.
$ coverage run ./runtests.py --settings=test_sqlite --parallel=1
...\> coverage run runtests.py --settings=test_sqlite --parallel=1
적용 범위를 실행한 후 다음을 실행하여 html 보고서를 생성합니다.
$ coverage html
...\> coverage html
Django 테스트에 대한 적용 범위를 실행할 때 포함된 “.coverager” 설정 파일은 “coverage_html”을 보고서의 출력 디렉터리로 정의하고 결과와 관련이 없는 여러 디렉터리(테스트 코드 또는 Django에 포함된 외부 코드)도 제외합니다.
기여 어플리케이션¶
기여 앱에 대한 테스트는 일반적으로 “_tests” 아래에 있는 “tests/” 디렉터리에서 찾을 수 있습니다. 예를 들어 ‘’contrib.auth’’에 대한 검정은(는) ‘’’`tests/auth_tests’’’에 있습니다.
문제해결¶
테스트 제품군이 ‘’main’’ 분기에 중단되거나 오류가 표시됩니다.¶
이전 버전에는 테스트 제품군의 실패 또는 행업 원인이 될 수 있는 버그가 종종 있으므로 a:ref:’supported Python version 의 최신 포인트 릴리스가 있는지 확인합니다.
**macOS**(High Sierra 이상 버전)에서는 다음 메시지가 기록되고 그 후에 테스트가 중단될 수 있습니다.
objc[42074]: +[__NSPlaceholderDate initialize] may have been in progress in
another thread when fork() was called.
이 문제를 방지하려면 다음과 같이 ``OBJC_DISABLE_INITIALIZE_FORK_SAFETY` 환경 변수를 설정하십시오.
$ OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES ./runtests.py
Or add export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
to your shell’s
startup file (e.g. ~/.profile
).
‘’UnicodeError’’로 많은 테스트가 실패합니다.¶
“locales” 패키지가 설치되지 않은 경우 일부 테스트는 “UnicodeError”와 함께 실패합니다.
예를 들어 Debian 기반 시스템에서 다음을 실행하여 이 문제를 해결할 수 있습니다.
$ apt-get install locales
$ dpkg-reconfigure locales
macOS 시스템의 경우 셸의 로케일을 구성하여 이 문제를 해결할 수 있습니다.
$ export LANG="en_US.UTF-8"
$ export LC_ALL="en_US.UTF-8"
“locale” 명령을 실행하여 변경 내용을 확인합니다. 선택적으로 이러한 내보내기 명령을 셸의 시작 파일(예: Bash의 경우 “~/.bashrc’’)에 추가하여 다시 입력할 필요가 없도록 합니다.
조합상으로만 실패하는 테스트입니다.¶
별도로 실행했을 때 테스트는 통과했지만 전체 제품군 내에서 실패할 경우 문제를 분석하는 데 도움이 되는 몇 가지 도구가 있습니다.
runtests.py``의 ``--bisect
옵션은 실패 테스트를 실행하는 동시에 각 반복에서 함께 실행되는 테스트 세트를 절반으로 줄여 실패와 관련이 있을 수 있는 적은 수의 테스트를 식별할 수 있게 합니다.
예를 들어, 자체적으로 작동하는 실패한 테스트가 ‘’ModelTest.test_eq’’라고 가정하고 다음을 사용합니다.
$ ./runtests.py --bisect basic.tests.ModelTest.test_eq
...\> runtests.py --bisect basic.tests.ModelTest.test_eq
지정된 테스트를 방해하는 테스트를 결정합니다. 먼저 테스트 스위트의 전반부에서 테스트를 실행합니다. 오류가 발생하면 테스트 스위트의 전반부가 두 그룹으로 분할되고 각 그룹이 지정된 테스트와 함께 실행됩니다. 테스트 스위트의 전반부에 고장이 없는 경우, 테스트 스위트의 후반부는 앞에서 설명한 대로 지정된 테스트와 함께 실행되고 적절히 분할됩니다. 이 프로세스는 실패한 테스트 집합이 최소화될 때까지 반복됩니다.
“–pair” 옵션은 제품군의 다른 모든 테스트와 함께 주어진 테스트를 실행하므로 다른 테스트에 실패의 원인이 되는 부작용이 있는지 확인할 수 있습니다. 그래서:
$ ./runtests.py --pair basic.tests.ModelTest.test_eq
...\> runtests.py --pair basic.tests.ModelTest.test_eq
“test_eq”는 모든 테스트 레이블과 쌍을 이룹니다.
“–bisect” 및 “–pair”를 모두 사용할 경우, 어떤 사례가 실패의 원인이 될 수 있는지 이미 의심하는 경우, 첫 번째 테스트 이후에 :ref:`specifying further test labels `가 교차 분석하도록 테스트를 제한할 수 있습니다.
$ ./runtests.py --pair basic.tests.ModelTest.test_eq queries transactions
...\> runtests.py --pair basic.tests.ModelTest.test_eq queries transactions
또한 “–shuffle” 및 “–reverse” 옵션을 사용하여 임의의 테스트 세트를 랜덤 또는 역순으로 실행할 수 있습니다. 이렇게 하면 테스트를 다른 순서로 실행해도 문제가 발생하지 않는지 확인할 수 있습니다.
$ ./runtests.py basic --shuffle
$ ./runtests.py basic --reverse
...\> runtests.py basic --shuffle
...\> runtests.py basic --reverse
테스트 중에 실행되는 SQL 쿼리를 봅니다.¶
실패한 테스트에서 실행 중인 SQL을 검사하려면 A 옵션을 사용하여 “–debug-sql”을 설정할 수 있습니다. 이 값을 “–verbosity=2”와 결합하면 모든 SQL 쿼리가 출력됩니다.
$ ./runtests.py basic --debug-sql
...\> runtests.py basic --debug-sql
테스트 실패의 전체 추적 결과를 봅니다.¶
기본적으로 테스트는 코어당 하나의 프로세스와 병렬로 실행됩니다. 그러나 테스트가 병렬로 실행되면 테스트 실패에 대한 잘린 추적만 볼 수 있습니다. “–parallel” 옵션을 사용하여 이 동작을 조정할 수 있습니다.
$ ./runtests.py basic --parallel=1
...\> runtests.py basic --parallel=1
:envvar를 사용할 수도 있습니다.이 목적을 위한 “DJANGO_TEST_PROCESS” 환경 변수입니다.
쓰기 테스트에 대한 팁입니다.¶
모델 등록을 격리합니다.¶
글로벌 :attr:’~django.apps.apps의 레지스트리를 오염시키지 않고 불필요한 테이블 생성을 방지하려면 테스트 방법에 정의된 모델을 임시 “Apps” 인스턴스에 바인딩해야 합니다.
from django.apps.registry import Apps
from django.db import models
from django.test import SimpleTestCase
class TestModelDefinition(SimpleTestCase):
def test_model_definition(self):
test_apps = Apps(['app_label'])
class TestModel(models.Model):
class Meta:
apps = test_apps
...
-
django.test.utils.
isolate_apps
(*app_labels, attr_name=None, kwarg_name=None)¶
이 패턴은 보일러 플레이트를 많이 사용하기 때문에 Django는 :func:’~django.test.utils.isolate_apps의 데코레이터를 제공합니다. 다음과 같이 사용됩니다.
from django.db import models
from django.test import SimpleTestCase
from django.test.utils import isolate_apps
class TestModelDefinition(SimpleTestCase):
@isolate_apps('app_label')
def test_model_definition(self):
class TestModel(models.Model):
pass
...
‘’app_label’’을 설정합니다.
명시적 app_label
이 없는 테스트 방법에 정의된 모델에는 테스트 클래스가 위치한 앱의 레이블이 자동으로 할당됩니다.
isolate_apps()
인스턴스의 컨텍스트에 정의된 모델이 올바르게 설치되었는지 확인하려면 대상 app_label
집합을 인수로 전달해야 합니다.
tests/app_label/tests.py
¶from django.db import models
from django.test import SimpleTestCase
from django.test.utils import isolate_apps
class TestModelDefinition(SimpleTestCase):
@isolate_apps('app_label', 'other_app_label')
def test_model_definition(self):
# This model automatically receives app_label='app_label'
class TestModel(models.Model):
pass
class OtherAppModel(models.Model):
class Meta:
app_label = 'other_app_label'
...
데코레이터는 클래스에도 적용할 수 있습니다.
from django.db import models
from django.test import SimpleTestCase
from django.test.utils import isolate_apps
@isolate_apps('app_label')
class TestModelDefinition(SimpleTestCase):
def test_model_definition(self):
class TestModel(models.Model):
pass
...
모델 등록을 분리하는 데 사용되는 임시 Apps
인스턴스는``attr_name`` 매개 변수를 사용하여 클래스 데코레이터로 사용될 때 속성으로 검색할 수 있습니다.
from django.db import models
from django.test import SimpleTestCase
from django.test.utils import isolate_apps
@isolate_apps('app_label', attr_name='apps')
class TestModelDefinition(SimpleTestCase):
def test_model_definition(self):
class TestModel(models.Model):
pass
self.assertIs(self.apps.get_model('app_label', 'TestModel'), TestModel)
또는 “kwarg_name” 매개 변수를 사용하여 메서드 데코레이터로 사용할 때 테스트 방법에 대한 인수로 사용합니다.
from django.db import models
from django.test import SimpleTestCase
from django.test.utils import isolate_apps
class TestModelDefinition(SimpleTestCase):
@isolate_apps('app_label', kwarg_name='apps')
def test_model_definition(self, apps):
class TestModel(models.Model):
pass
self.assertIs(apps.get_model('app_label', 'TestModel'), TestModel)