DB를 설정하지 않고 장고 단위 테스트를 작성할 가능성이 있습니까? db를 설정하지 않아도되는 비즈니스 로직을 테스트하고 싶습니다. 그리고 db를 설정하는 것이 빠르지 만 실제로는 상황에 따라 필요하지 않습니다.
DB를 설정하지 않고 장고 단위 테스트를 작성할 가능성이 있습니까? db를 설정하지 않아도되는 비즈니스 로직을 테스트하고 싶습니다. 그리고 db를 설정하는 것이 빠르지 만 실제로는 상황에 따라 필요하지 않습니다.
답변:
DjangoTestSuiteRunner를 서브 클래 싱하고 setup_databases 및 teardown_databases 메소드를 대체하여 전달할 수 있습니다.
새 설정 파일을 작성하고 TEST_RUNNER를 방금 작성한 새 클래스로 설정하십시오. 그런 다음 테스트를 실행할 때 --settings 플래그를 사용하여 새 설정 파일을 지정하십시오.
여기 내가 한 일이 있습니다.
다음과 유사한 사용자 정의 테스트 슈트 러너를 작성하십시오.
from django.test.simple import DjangoTestSuiteRunner
class NoDbTestRunner(DjangoTestSuiteRunner):
""" A test runner to test without database creation """
def setup_databases(self, **kwargs):
""" Override the database creation defined in parent class """
pass
def teardown_databases(self, old_config, **kwargs):
""" Override the database teardown defined in parent class """
pass
사용자 정의 설정을 작성하십시오.
from mysite.settings import *
# Test runner with no database creation
TEST_RUNNER = 'mysite.scripts.testrunner.NoDbTestRunner'
테스트를 실행할 때 --settings 플래그를 새 설정 파일로 설정하여 다음과 같이 실행하십시오.
python manage.py test myapp --settings='no_db_settings'
업데이트 : 2018 년 4 월
Django 1.8부터 모듈 이로 이동 했습니다 .django.test.simple.DjangoTestSuiteRunner
'django.test.runner.DiscoverRunner'
자세한 내용 은 사용자 정의 테스트 러너에 대한 공식 문서 섹션을 확인하십시오 .
--testrunner
옵션을 사용하여 새 TestRunner를 지정할 수 있습니다 .
일반적으로 응용 프로그램의 테스트는 두 가지 범주로 분류 할 수 있습니다
Django는 단위 테스트와 통합 테스트를 모두 지원합니다.
단위 테스트는 데이터베이스를 설정 및 해제 할 필요가 없으며 SimpleTestCase 에서 상속해야합니다 .
from django.test import SimpleTestCase
class ExampleUnitTest(SimpleTestCase):
def test_something_works(self):
self.assertTrue(True)
통합 테스트 케이스의 경우 TestCase에서 상속하고 TransactionTestCase에서 상속하며 각 테스트를 실행하기 전에 데이터베이스를 설정 및 해제합니다.
from django.test import TestCase
class ExampleIntegrationTest(TestCase):
def test_something_works(self):
#do something with database
self.assertTrue(True)
이 전략은 데이터베이스에 액세스하는 테스트 케이스에 대해서만 데이터베이스를 작성하고 파괴하므로 테스트가 더 효율적입니다.
에서 django.test.simple
warnings.warn(
"The django.test.simple module and DjangoTestSuiteRunner are deprecated; "
"use django.test.runner.DiscoverRunner instead.",
RemovedInDjango18Warning)
따라서 DiscoverRunner
대신에 재정의하십시오 DjangoTestSuiteRunner
.
from django.test.runner import DiscoverRunner
class NoDbTestRunner(DiscoverRunner):
""" A test runner to test without database creation/deletion """
def setup_databases(self, **kwargs):
pass
def teardown_databases(self, old_config, **kwargs):
pass
그런 식으로 사용하십시오 :
python manage.py test app --testrunner=app.filename.NoDbTestRunner
나는 메소드 를 상속 django.test.runner.DiscoverRunner
하고 run_tests
메소드에 몇 가지를 추가하기로 결정했습니다 .
첫 번째 추가 항목은 DB 설정이 필요한지 setup_databases
확인하고 DB가 필요한 경우 정상적인 기능을 시작하도록 허용합니다 . 두 번째 추가는 메소드 실행이 허용 된 teardown_databases
경우 정규 실행 setup_databases
을 허용합니다.
내 코드는 상속 된 TestCase django.test.TransactionTestCase
(및 따라서 django.test.TestCase
)에서 데이터베이스를 설정해야 한다고 가정합니다 . 장고 문서는 다음과 같이 말했기 때문에이 가정을했습니다.
... ORM 테스트 또는 사용 ...과 같이 더 복잡하고 무거운 Django 관련 기능이 필요한 경우 TransactionTestCase 또는 TestCase를 대신 사용해야합니다.
https://docs.djangoproject.com/en/1.6/topics/testing/tools/#django.test.SimpleTestCase
from django.test import TransactionTestCase
from django.test.runner import DiscoverRunner
class MyDiscoverRunner(DiscoverRunner):
def run_tests(self, test_labels, extra_tests=None, **kwargs):
"""
Run the unit tests for all the test labels in the provided list.
Test labels should be dotted Python paths to test modules, test
classes, or test methods.
A list of 'extra' tests may also be provided; these tests
will be added to the test suite.
If any of the tests in the test suite inherit from
``django.test.TransactionTestCase``, databases will be setup.
Otherwise, databases will not be set up.
Returns the number of tests that failed.
"""
self.setup_test_environment()
suite = self.build_suite(test_labels, extra_tests)
# ----------------- First Addition --------------
need_databases = any(isinstance(test_case, TransactionTestCase)
for test_case in suite)
old_config = None
if need_databases:
# --------------- End First Addition ------------
old_config = self.setup_databases()
result = self.run_suite(suite)
# ----------------- Second Addition -------------
if need_databases:
# --------------- End Second Addition -----------
self.teardown_databases(old_config)
self.teardown_test_environment()
return self.suite_result(suite, result)
마지막으로 프로젝트의 settings.py 파일에 다음 줄을 추가했습니다.
TEST_RUNNER = 'mysite.scripts.settings.MyDiscoverRunner'
이제 비 DB 의존 테스트 만 실행할 때 테스트 스위트가 훨씬 빠르게 실행됩니다! :)
업데이트 됨 : 타사 도구 사용에 대한 이 답변 도 참조하십시오 pytest
.
@Cesar가 맞습니다. ./manage.py test --settings=no_db_settings
앱 이름을 지정하지 않고 실수로를 실행 한 후 개발 데이터베이스가 지워졌습니다.
보다 안전한 방식으로 NoDbTestRunner
다음을 함께 사용하십시오 mysite/no_db_settings.py
.
from mysite.settings import *
# Test runner with no database creation
TEST_RUNNER = 'mysite.scripts.testrunner.NoDbTestRunner'
# Use an alternative database as a safeguard against accidents
DATABASES['default']['NAME'] = '_test_mysite_db'
_test_mysite_db
외부 데이터베이스 도구를 사용하여 호출 된 데이터베이스를 작성해야합니다 . 그런 다음 다음 명령을 실행하여 해당 테이블을 작성하십시오.
./manage.py syncdb --settings=mysite.no_db_settings
South를 사용하는 경우 다음 명령도 실행하십시오.
./manage.py migrate --settings=mysite.no_db_settings
확인!
이제 다음을 통해 단위 테스트를 엄청나게 빠르고 안전하게 실행할 수 있습니다.
./manage.py test myapp --settings=mysite.no_db_settings
NoDbTestRunner를 "안전"하도록 설정을 수정하는 대신 현재 데이터베이스 연결을 닫고 설정 및 연결 개체에서 연결 정보를 제거하는 수정 된 버전의 NoDbTestRunner가 있습니다. 나를 위해 일하고, 의존하기 전에 환경에서 테스트하십시오 :)
class NoDbTestRunner(DjangoTestSuiteRunner):
""" A test runner to test without database creation """
def __init__(self, *args, **kwargs):
# hide/disconnect databases to prevent tests that
# *do* require a database which accidentally get
# run from altering your data
from django.db import connections
from django.conf import settings
connections.databases = settings.DATABASES = {}
connections._connections['default'].close()
del connections._connections['default']
super(NoDbTestRunner,self).__init__(*args,**kwargs)
def setup_databases(self, **kwargs):
""" Override the database creation defined in parent class """
pass
def teardown_databases(self, old_config, **kwargs):
""" Override the database teardown defined in parent class """
pass
__getitem__
더 이상 지원하지 않는 것 같습니다 . connections._connections.default
객체에 접근하기 위해 사용 합니다.
또 다른 해결책은 테스트 클래스 unittest.TestCase
가 Django의 테스트 클래스 대신 상속되는 것입니다. 장고 문서 ( https://docs.djangoproject.com/en/2.0/topics/testing/overview/#writing-tests )에는 다음에 대한 경고가 포함되어 있습니다.
unittest.TestCase를 사용하면 트랜잭션에서 각 테스트를 실행하고 데이터베이스를 플러시하는 비용을 피할 수 있지만 테스트가 데이터베이스와 상호 작용하는 경우 테스트 실행기가 실행하는 순서에 따라 동작이 달라집니다. 이로 인해 단독으로 실행될 때 통과하지만 스위트에서 실행될 때 실패하는 단위 테스트가 발생할 수 있습니다.
그러나 테스트에서 데이터베이스를 사용하지 않는 경우이 경고는 걱정할 필요가 없으며 트랜잭션에서 각 테스트 사례를 실행할 필요가 없다는 이점을 얻을 수 있습니다.
내 웹 호스트는 웹 GUI에서 데이터베이스를 만들고 삭제할 수만 있으므로 실행하려고 할 때 "테스트 데이터베이스를 만드는 중 오류가 발생했습니다 : 권한이 거부되었습니다"라는 오류가 발생했습니다 python manage.py test
.
django-admin.py에 --keepdb 옵션을 사용하고 싶었지만 Django 1.7에서 더 이상 지원되지 않는 것 같습니다.
내가 한 일은 ... / django / db / backends / creation.py의 Django 코드, 특히 _create_test_db 및 _destroy_test_db 함수를 수정하는 것이 었습니다.
들어 _create_test_db
나는 주석 cursor.execute("CREATE DATABASE ...
행을하고 그것을 대체 pass
그렇게 try
블록이 비어있을 수 없습니다 것입니다.
들어 _destroy_test_db
난 그냥 주석 cursor.execute("DROP DATABASE
- 다른 명령 블록에 이미 있었기 때문에 나는 아무것도로 교체 할 필요가 없었다 ( time.sleep(1)
).
그 후 내 테스트는 정상적으로 실행되었지만 일반 데이터베이스의 test_ 버전을 별도로 설정했습니다.
Django를 업그레이드하면 깨질 수 있기 때문에 물론 훌륭한 해결책은 아니지만 virtualenv를 사용하여 Django의 로컬 사본을 가지고 있으므로 적어도 최신 버전으로 업그레이드 할 때 / 경우를 제어 할 수 있습니다.
언급되지 않은 또 다른 솔루션 : base.py에서 상속받은 여러 설정 파일 (로컬 / 준비 / 프로덕션 용)이 이미 있기 때문에 구현하기가 쉽습니다. 따라서 다른 사람들과 달리 DATABASES가 base.py에 설정되어 있지 않으므로 DATABASES [ 'default']를 덮어 쓸 필요가 없었습니다.
SimpleTestCase는 여전히 테스트 데이터베이스에 연결하고 마이그레이션을 실행하려고했습니다. DATABASES를 설정하지 않은 config / settings / test.py 파일을 만들었을 때 단위 테스트없이 파일을 실행했습니다. 외래 키와 고유 제약 조건 필드가있는 모델을 사용할 수있었습니다. (DB 조회가 필요한 역방향 외래 키 조회가 실패합니다.)
(장고 2.0.6)
PS 코드 스 니펫
PROJECT_ROOT_DIR/config/settings/test.py:
from .base import *
#other test settings
#DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': 'PROJECT_ROOT_DIR/db.sqlite3',
# }
#}
cli, run from PROJECT_ROOT_DIR:
./manage.py test path.to.app.test --settings config.settings.test
path/to/app/test.py:
from django.test import SimpleTestCase
from .models import *
#^assume models.py imports User and defines Classified and UpgradePrice
class TestCaseWorkingTest(SimpleTestCase):
def test_case_working(self):
self.assertTrue(True)
def test_models_ok(self):
obj = UpgradePrice(title='test',price=1.00)
self.assertEqual(obj.title,'test')
def test_more_complex_model(self):
user = User(username='testuser',email='hi@hey.com')
self.assertEqual(user.username,'testuser')
def test_foreign_key(self):
user = User(username='testuser',email='hi@hey.com')
ad = Classified(user=user,headline='headline',body='body')
self.assertEqual(ad.user.username,'testuser')
#fails with error:
def test_reverse_foreign_key(self):
user = User(username='testuser',email='hi@hey.com')
ad = Classified(user=user,headline='headline',body='body')
print(user.classified_set.first())
self.assertTrue(True) #throws exception and never gets here
코 테스트 러너 (장고 코)를 사용할 때 다음과 같이 할 수 있습니다.
my_project/lib/nodb_test_runner.py
:
from django_nose import NoseTestSuiteRunner
class NoDbTestRunner(NoseTestSuiteRunner):
"""
A test runner to test without database creation/deletion
Used for integration tests
"""
def setup_databases(self, **kwargs):
pass
def teardown_databases(self, old_config, **kwargs):
pass
당신에 settings.py
당신이 거기 테스트 러너를 지정할 수 있습니다, 즉,
TEST_RUNNER = 'lib.nodb_test_runner.NoDbTestRunner' . # Was 'django_nose.NoseTestSuiteRunner'
또는
특정 테스트 만 실행하기를 원했기 때문에 다음과 같이 실행했습니다.
python manage.py test integration_tests/integration_* --noinput --testrunner=lib.nodb_test_runner.NoDbTestRunner