【问题标题】:How to launch tests for django reusable app?如何为 django 可重用应用程序启动测试?
【发布时间】:2011-04-19 23:53:25
【问题描述】:

我可以在不将该应用程序合并到项目中的情况下为我的可重用 Django 应用程序启动测试吗?

我的应用使用了一些机型,所以需要提供(TEST_)DATABASE_*设置。我应该将它们存储在哪里以及如何启动测试?

对于 Django 项目,我可以使用 manage.py test 运行测试;当我将django-admin.py test 与我的独立应用程序一起使用时,我得到:

错误:无法导入设置, 因为环境变量 DJANGO_SETTINGS_MODULE 未定义。

这里有哪些最佳做法?

【问题讨论】:

  • Django 不需要项目。你在问什么?你运行django-admin.py test了吗?你观察到了什么?
  • 对于 django 项目,我通过以下方式运行测试:manage.py 测试,当我使用 django-admin.py 时,我得到:错误:无法导入设置,因为环境变量 DJANGO_SETTINGS_MODULE 未定义。我相信这必须非常简单,但我坚持这样做。

标签: django testing django-testing


【解决方案1】:

Django (>= 1.4) test runner的正确用法如下:

import django, sys
from django.conf import settings

settings.configure(DEBUG=True,
               DATABASES={
                    'default': {
                        'ENGINE': 'django.db.backends.sqlite3',
                    }
                },
               ROOT_URLCONF='myapp.urls',
               INSTALLED_APPS=('django.contrib.auth',
                              'django.contrib.contenttypes',
                              'django.contrib.sessions',
                              'django.contrib.admin',
                              'myapp',))

try:
    # Django < 1.8
    from django.test.simple import DjangoTestSuiteRunner
    test_runner = DjangoTestSuiteRunner(verbosity=1)
except ImportError:
    # Django >= 1.8
    django.setup()
    from django.test.runner import DiscoverRunner
    test_runner = DiscoverRunner(verbosity=1)

failures = test_runner.run_tests(['myapp'])
if failures:
    sys.exit(failures)

DjangoTestSuiteRunner 和 DiscoverRunner 具有大部分兼容的接口。

有关更多信息,您应该查阅“定义测试运行者”文档:

【讨论】:

    【解决方案2】:

    我以这样的解决方案结束(它的灵感来自 django-voting 中的解决方案):

    创建文件,例如。测试目录中的“runtests.py”包含:

    import os, sys
    from django.conf import settings
    
    DIRNAME = os.path.dirname(__file__)
    settings.configure(DEBUG = True,
                       DATABASE_ENGINE = 'sqlite3',
                       DATABASE_NAME = os.path.join(DIRNAME, 'database.db'),
                       INSTALLED_APPS = ('django.contrib.auth',
                                         'django.contrib.contenttypes',
                                         'django.contrib.sessions',
                                         'django.contrib.admin',
                                         'myapp',
                                         'myapp.tests',))
    
    
    from django.test.simple import run_tests
    
    failures = run_tests(['myapp',], verbosity=1)
    if failures:
        sys.exit(failures)
    

    它允许通过python runtests.py 命令运行测试。 它不需要安装依赖项(例如 buildout),并且不会损害将应用程序合并到更大项目中时运行的测试。

    【讨论】:

      【解决方案3】:

      对于 Django 1.7,它略有不同。假设你有以下 应用程序foo的目录结构:

      foo
      |── docs
      |── foo
      │   ├── __init__.py
      │   ├── models.py
      │   ├── urls.py
      │   └── views.py
      └── tests
          ├── foo_models
          │   ├── __init__.py
          │   ├── ...
          │   └── tests.py
          ├── foo_views 
          │   ├── __init__.py
          │   ├── ...
          │   └── tests.py
          ├── runtests.py
          └── urls.py
      

      这就是 Django 项目本身构建其测试的方式。

      您想使用以下命令在foo/tests/ 中运行所有测试:

      python3 runtests.py
      

      您还希望能够从 tests 的父目录运行命令,例如通过 Tox 或 Invoke,就像 python3 foo/tests/runtests.py

      我在这里介绍的解决方案是可重复使用的,只需调整应用程序的名称foo(如果需要,还需要调整其他应用程序)。它们可以通过modify_settings 安装,因为它会错过数据库设置。

      需要以下文件:

      urls.py

      """
      This urlconf exists because Django expects ROOT_URLCONF to exist. URLs
      should be added within the test folders, and use TestCase.urls to set them.
      This helps the tests remain isolated.
      """
      
      urlpatterns = []
      

      runtests.py

      #!/usr/bin/env python3
      import glob
      import os
      import sys
      
      import django
      from django.conf import settings
      from django.core.management import execute_from_command_line
      
      
      BASE_DIR = os.path.abspath(os.path.dirname(__file__))
      sys.path.append(os.path.abspath(os.path.join(BASE_DIR, '..')))
      
      # Unfortunately, apps can not be installed via ``modify_settings``
      # decorator, because it would miss the database setup.
      CUSTOM_INSTALLED_APPS = (
          'foo',
          'django.contrib.admin',
      )
      
      ALWAYS_INSTALLED_APPS = (
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.messages',
          'django.contrib.staticfiles',
      )
      
      ALWAYS_MIDDLEWARE_CLASSES = (
          'django.contrib.sessions.middleware.SessionMiddleware',
          'django.middleware.common.CommonMiddleware',
          'django.middleware.csrf.CsrfViewMiddleware',
          'django.contrib.auth.middleware.AuthenticationMiddleware',
          'django.contrib.messages.middleware.MessageMiddleware',
          'django.middleware.clickjacking.XFrameOptionsMiddleware',
      )
      
      
      settings.configure(
          SECRET_KEY="django_tests_secret_key",
          DEBUG=False,
          TEMPLATE_DEBUG=False,
          ALLOWED_HOSTS=[],
          INSTALLED_APPS=ALWAYS_INSTALLED_APPS + CUSTOM_INSTALLED_APPS,
          MIDDLEWARE_CLASSES=ALWAYS_MIDDLEWARE_CLASSES,
          ROOT_URLCONF='tests.urls',
          DATABASES={
              'default': {
                  'ENGINE': 'django.db.backends.sqlite3',
              }
          },
          LANGUAGE_CODE='en-us',
          TIME_ZONE='UTC',
          USE_I18N=True,
          USE_L10N=True,
          USE_TZ=True,
          STATIC_URL='/static/',
          # Use a fast hasher to speed up tests.
          PASSWORD_HASHERS=(
              'django.contrib.auth.hashers.MD5PasswordHasher',
          ),
          FIXTURE_DIRS=glob.glob(BASE_DIR + '/' + '*/fixtures/')
      
      )
      
      django.setup()
      args = [sys.argv[0], 'test']
      # Current module (``tests``) and its submodules.
      test_cases = '.'
      
      # Allow accessing test options from the command line.
      offset = 1
      try:
          sys.argv[1]
      except IndexError:
          pass
      else:
          option = sys.argv[1].startswith('-')
          if not option:
              test_cases = sys.argv[1]
              offset = 2
      
      args.append(test_cases)
      # ``verbosity`` can be overwritten from command line.
      args.append('--verbosity=2')
      args.extend(sys.argv[offset:])
      
      execute_from_command_line(args)
      

      有些设置是可选的;它们可以提高速度或更逼真的环境。

      第二个参数指向当前目录。它使用feature of providing a path to a directory to discover tests below that directory

      【讨论】:

      • 很好的答案!我在/tests/ 中添加了__init__.py 以使其正常工作。
      • 在大多数情况下,我能够避免在tests 目录中为我的应用程序设置子目录,只需设置test_cases = '..' (以允许我遵循进行测试的约定.py 在应用程序文件夹中)。很好的答案!
      【解决方案4】:

      对于我的可重用应用程序 (django-moderation),我使用 buildout。我创建了example_project,我用它和 buildout 来运行测试。我只是将我的应用程序放在example_project 的设置中。

      当我想安装我的项目使用的所有依赖项并运行测试时,我只需要执行以下操作:

      • 运行:python bootstrap.py
      • 运行构建:

        bin/构建

      • 为 Django 1.1 和 Django 1.2 运行测试:

        bin/test-1.1 bin/test-1.2

      在这里您可以找到如何配置可重用应用程序以使用 buildout 进行部署和测试运行的教程:http://jacobian.org/writing/django-apps-with-buildout/

      在这里您将找到我在项目中使用的示例构建配置:

      http://github.com/dominno/django-moderation/blob/master//buildout.cfg

      【讨论】:

      • 感谢您提供有用的信息。看看这是否能解决我的问题(乍一看似乎很麻烦),但对于其他目的可能很有趣。
      【解决方案5】:

      在纯 pytest 上下文中,为了提供足够的 Django 环境来让我的可重用应用程序在没有实际 Django 项目的情况下运行测试,我需要以下部分:

      pytest.ini:

      [pytest]
      DJANGO_SETTINGS_MODULE = test_settings
      python_files = tests.py test_*.py *_tests.py
      

      test_settings.py:

      # You may need more or less than what's shown here - this is a skeleton:
      
      DATABASES = {
          'default': {
              'ENGINE': 'django.db.backends.sqlite3',
          }
      }
      
      INSTALLED_APPS = (
          'django.contrib.admin',
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.messages',
          'django.contrib.sessions',
          'django.contrib.sites',
          'django.contrib.staticfiles',
          'todo',
      )
      
      ROOT_URLCONF = 'base_urls'
      
      TEMPLATES = [
          {
              'DIRS': ['path/to/your/templates'), ],
          }
      ]
      
      MIDDLEWARE = [
          'django.middleware.security.SecurityMiddleware',
          'django.contrib.sessions.middleware.SessionMiddleware',
          'django.middleware.common.CommonMiddleware',
          'django.middleware.csrf.CsrfViewMiddleware',
          'django.contrib.auth.middleware.AuthenticationMiddleware',
          'django.contrib.messages.middleware.MessageMiddleware',
          'django.middleware.clickjacking.XFrameOptionsMiddleware',
      ]
      

      base_urls.py:

      """
      This urlconf exists so we can run tests without an actual
      Django project (Django expects ROOT_URLCONF to exist.)
      It is not used by installed instances of this app.
      """
      
      from django.urls import include, path
      
      urlpatterns = [
          path('foo/', include('myapp.urls')),
      ]
      

      模板/base.html:

      如果您的任何测试达到实际视图,您的应用程序模板可能会扩展项目base.html,因此该文件必须存在。就我而言,我刚刚创建了一个空文件templates/base.html

      我现在可以从我的独立可重用应用程序目录中运行 pytest -x -v,而无需 Django 项目。

      【讨论】:

        【解决方案6】:

        我知道这是一个旧线程,但我发现如果您的可重用应用程序有一个文件“tests.py”,那么您可以在项目文件夹中创建一个tests.py,然后编写以下内容...

        from reusableapp.tests import *
        

        当你调用“python manage.py test”时,Django 会运行​​这些测试

        【讨论】:

        • 我将 print("running reusableapp tests...") 放在可重用应用程序的 tests.py 顶部,这样我可以看到正在运行哪些测试。
        猜你喜欢
        • 2014-12-18
        • 1970-01-01
        • 2012-03-14
        • 2013-02-12
        • 2020-06-26
        • 1970-01-01
        • 2015-03-02
        • 1970-01-01
        • 2014-04-01
        相关资源
        最近更新 更多