[django] 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'

자세한 내용 은 사용자 정의 테스트 러너에 대한 공식 문서 섹션을 확인하십시오 .


답변

일반적으로 응용 프로그램의 테스트는 두 가지 범주로 분류 할 수 있습니다

  1. 단위 테스트-이러한 코드는 일사량의 개별 코드 스 니펫을 테스트하며 데이터베이스로 이동할 필요가 없습니다.
  2. 실제로 데이터베이스로 이동하여 완전히 통합 된 로직을 테스트하는 통합 테스트 사례.

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

mysite / scripts / settings.py

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 파일에 다음 줄을 추가했습니다.

mysite / 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


답변

또 다른 해결책은 테스트 클래스 unittest.TestCase가 Django의 테스트 클래스 대신 상속되는 것입니다. 장고 문서 ( https://docs.djangoproject.com/en/2.0/topics/testing/overview/#writing-tests )에는 다음에 대한 경고가 포함되어 있습니다.

unittest.TestCase를 사용하면 트랜잭션에서 각 테스트를 실행하고 데이터베이스를 플러시하는 비용을 피할 수 있지만 테스트가 데이터베이스와 상호 작용하는 경우 테스트 실행기가 실행하는 순서에 따라 동작이 달라집니다. 이로 인해 단독으로 실행될 때 통과하지만 스위트에서 실행될 때 실패하는 단위 테스트가 발생할 수 있습니다.

그러나 테스트에서 데이터베이스를 사용하지 않는 경우이 경고는 걱정할 필요가 없으며 트랜잭션에서 각 테스트 사례를 실행할 필요가 없다는 이점을 얻을 수 있습니다.