2016-12-20 4 views
1

У меня есть проект Django (1.10.2) («theproject») и некоторые из них ведут себя (0.4.0). Я использовал behave-django. python manage.py behave работ. Тем не менее, PyCharm (which uses the behave executable rather than a Django management command) doesn't know how to run my features, поэтому я пытаюсь использовать behave's documented "manual integration" with Django.Почему не документирована ручная интеграция с Django?

Мой весь features/environment.py:

import os 
import django 
from django.test.runner import DiscoverRunner 
from django.test.testcases import LiveServerTestCase 
from splinter.browser import Browser 

os.environ["DJANGO_SETTINGS_MODULE"] = "theproject.settings" 

def before_all(context): 
    django.setup() 
    context.test_runner = DiscoverRunner() 
    context.test_runner.setup_test_environment() 
    context.old_db_config = context.test_runner.setup_databases() 
    context.browser = Browser('phantomjs') 

    # When we're running with PhantomJS we need to specify the window size. 
    # This is a workaround for an issue where PhantomJS cannot find elements 
    # by text - see: https://github.com/angular/protractor/issues/585 
    if context.browser.driver_name == 'PhantomJS': 
     context.browser.driver.set_window_size(1280, 1024) 

def before_scenario(context, _): 
    context.test_case = LiveServerTestCase 
    context.test_case.setUpClass() 

def after_scenario(context, _): 
    context.test_case.tearDownClass() 
    del context.test_case 

def after_all(context): 
    context.test_runner.teardown_databases(context.old_db_config) 
    context.test_runner.teardown_test_environment() 

    context.browser.quit() 
    del context.browser 

Вот INSTALLED_APPS от theproject/setting.py в случае, если это полезно (я удалил 'behave-django' для этого эксперимента):

INSTALLED_APPS = [ 
    'django.contrib.auth', 
    'django.contrib.contenttypes', 
    'django.contrib.sessions', 
    'django.contrib.messages', 
    'django.contrib.staticfiles', 
    'django_extensions', 
    'oauth2_provider', 
    'push_notifications', 
    'raven.contrib.django.raven_compat', 
    'rest_framework', 
    'app1.apps.App1Config', 
    'app2', 
    'django.contrib.admin' # Must follow apps for apps' models to appear in admin UI 
] 

Когда я бегу behave я получаю

Exception AppRegistryNotReady: Apps aren't loaded yet. 
Traceback (most recent call last): 
    File "/usr/local/bin/behave", line 11, in <module> 
    sys.exit(main()) 
    File "/usr/local/lib/python2.7/site-packages/behave/__main__.py", line 109, in main 
    failed = runner.run() 
    File "/usr/local/lib/python2.7/site-packages/behave/runner.py", line 672, in run 
    return self.run_with_paths() 
    File "/usr/local/lib/python2.7/site-packages/behave/runner.py", line 678, in run_with_paths 
    self.load_step_definitions() 
    File "/usr/local/lib/python2.7/site-packages/behave/runner.py", line 658, in load_step_definitions 
    exec_file(os.path.join(path, name), step_module_globals) 
    File "/usr/local/lib/python2.7/site-packages/behave/runner.py", line 304, in exec_file 
    exec(code, globals, locals) 
    File "features/steps/common.py", line 5, in <module> 
    from django.contrib.auth.models import User 
    File "/usr/local/lib/python2.7/site-packages/django/contrib/auth/models.py", line 4, in <module> 
    from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager 
    File "/usr/local/lib/python2.7/site-packages/django/contrib/auth/base_user.py", line 52, in <module> 
    class AbstractBaseUser(models.Model): 
    File "/usr/local/lib/python2.7/site-packages/django/db/models/base.py", line 105, in __new__ 
    app_config = apps.get_containing_app_config(module) 
    File "/usr/local/lib/python2.7/site-packages/django/apps/registry.py", line 237, in get_containing_app_config 
    self.check_apps_ready() 
    File "/usr/local/lib/python2.7/site-packages/django/apps/registry.py", line 124, in check_apps_ready 
    raise AppRegistryNotReady("Apps aren't loaded yet.") 
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet. 

Как этот способ интеграции django и поведения должен работать?

Что-то я попробовал, не работает (или не совсем): Я переехал django.setup() на верхний уровень environment.py, сразу же после установки DJANGO_SETTINGS_MODULE. Это фиксирует AppRegistryNotReady, но многие сценарии завершаться

IntegrityError: duplicate key value violates unique constraint "auth_user_username_key" 
DETAIL: Key (username)=(username) already exists. 

Под вести себя-Джанго транзакция была начата перед каждым scenarion и откат после; что, похоже, сейчас не происходит. LiveServerTestCase расширяет TransactionTestCase, поэтому я озадачен.

+0

** AppRegistryNotReady: Приложения еще не загружены. ** Эта ошибка возникает, когда приложения в установках INSTALLED_APPS не находятся в правильной иерархии. Я предлагаю вам проверить это. Если проблема по-прежнему сохраняется, пожалуйста, поделитесь настройками INSTALLED_APPS. –

+0

Также проверьте ответы на этот вопрос http://stackoverflow.com/questions/26276397/django-1-7-upgrade-error-appregistrynotready-apps-arent-loaded-yet и http://stackoverflow.com/questions/ 33186413/djangodjango-core-exceptions-appregistrynotready-apps-arent-loaded-yet –

+0

Спасибо за первое предложение, но мое приложение запускается, мои тесты django.test.TestCase проходят, и, когда я использую, когда я использую behave-django, мои действия ведут себя, поэтому кажется, что INSTALLED_APPS должен быть в порядке. Верный? –

ответ

2

Ваши изменения в базе данных не будет откат между сценариями (отсюда IntegrityError). База данных только снесена в after_all(). Попробуйте переместить весь код в функции before_scenario и after_scenario (см. related docs).

Время выполнения ваших тестов будет увеличиваться, но тесты будут изолированы. И в качестве быстрого решения это заставляет их пройти хотя бы по крайней мере. Советы для более чистых решений в комментариях и альтернативных ответах.

0

Я уверен, что ошибка на самом деле в

File "features/steps/common.py", line 5, in <module> 

который пытается загрузить пользователя перед загрузкой приложения. Попробуйте переместить импорт внутри step_impl.

+0

Спасибо за предложение. Это исправит «AppRegistryNotReady» в этот момент, но было бы неприятно импортировать модели на каждом шагу. 'Django.setup()' на верхнем уровне менее инвазивна. –

+0

Ну, это зависит от вашей философии :) Я лично предпочел бы иметь его на каждом шагу, но до вас. –

1

Вы, кажется, неправильно поняли, что TransactionTestCaseLiveServerTestCase). Относительно отношений. Это не тестовый пример, который использует транзакции для запуска тестов и откат изменений в конце (это то, что делает «обычный» Django «TestCase»). Это тест, который делает не использовать транзакции, чтобы вы можете проверить свои собственные операции:

класс TestCase Django является более широко используется подкласс TransactionTestCase, что делает использование транзакций базы данных объектов, чтобы ускорить процесс сброса базы данных в известное состояние в начале каждого теста. Следствием этого, однако, является то, что некоторые поведения базы данных не могут быть протестированы в классе Django TestCase. Например, вы не можете проверить, что блок кода выполняется в транзакции, как это требуется при использовании select_for_update(). В этих случаях вы должны использовать TransactionTestCase.

(source)

TransactionTestCase еще сбрасывает базу данных с помощью промывки всех таблиц, стреляя сигнал после перенастройки и переустановка приборов, но она работает в предположении, что __call__ вызывается на тестовом примере. Кажется, что behave is calling run() directly, поэтому он пропускает установку/отключение данных базы данных для каждого теста.

Возможно, вы исправите это, создав свой собственный подкласс, который вызывает _pre_setup() и _post_teardown() в setUp() и tearDown(). Единственное предостережение в том, что каждый подкласс, который переопределяет setUp() или tearDown()должен позвонить super(), или _pre_setup()/_post_teardown() не будет называться.

class BehaveLiveServerTestCase(LiveServerTestCase): 
    def setUp(self): 
     self._pre_setup() 

    def tearDown(self): 
     self._post_teardown() 
+0

Оказалось, что behave-django действительно вызывает '__call__' на тестовом примере, поэтому я пошел с этим. –

0

Вот что у меня получилось. Это позволяет моим функциям запускаться под командой behave без behave-django и, таким образом, запускаться в конфигурациях запуска PyCharm без изменения функций или шагов. BehaviorDrivenTestCase и звонки context.test() в before_scenario копируются из внутренних элементов behave-django. Я опускаю настройки браузера и разрывы и некоторые другие биты, не имеющие отношения к этому вопросу.

import os 
import django 
from django.contrib.staticfiles.testing import StaticLiveServerTestCase 
from django.core import management 
from django.shortcuts import resolve_url 
from django.test.runner import DiscoverRunner 

os.environ["DJANGO_SETTINGS_MODULE"] = "api.settings" 
django.setup() 

def before_all(context): 
    context.test_runner = DiscoverRunner() 
    context.test_runner.setup_test_environment() 
    context.old_db_config = context.test_runner.setup_databases() 

def before_scenario(context, _): 
    context.test = BehaviorDrivenTestCase() 
    context.test.setUpClass() 
    context.test() # this starts a transaction 

    context.base_url = context.test.live_server_url 
    def get_url(to=None, *args, **kwargs): 
     return context.base_url + (
      resolve_url(to, *args, **kwargs) if to else '') 
    context.get_url = get_url 

class BehaviorDrivenTestCase(StaticLiveServerTestCase): 
    """ 
    Test case attached to the context during behave execution 

    This test case prevents the regular tests from running. 
    """ 

    def runTest(*args, **kwargs): 
     pass 

def after_scenario(context, _): 
    context.test.tearDownClass() 
    del context.test 

def after_all(context): 
    context.test_runner.teardown_databases(context.old_db_config) 
    context.test_runner.teardown_test_environment() 

Разделительные сценарии в операциях не будет работать для сценариев, которые осуществляют пользовательский интерфейс, который использует асинхронный JavaScript. Удаление context.test() и добавление management.call_command('flush', verbosity=0, interactive=False) в начале before_scenario также работает, занимает примерно такое же количество времени и должно работать лучше с async Javascript.

Смежные вопросы