为什么 behave 与 Django 的文档化手动集成不起作用?
Why isn't behave's documented manual integration with Django working?
我有一个 Django (1.10.2) 项目(“theproject”)和一些行为 (0.4.0) 功能。我一直在使用 behave-django。 python manage.py behave
有效。然而,, so I'm attempting to use behave's documented "manual integration" with 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
这里是 theproject/setting.py
的 INSTALLED_APPS
以防有用(我为这个实验删除了 '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和behavior集成的方式如何实现?
我尝试过但没有奏效(或不完全)的方法:我在设置 DJANGO_SETTINGS_MODULE
之后将 django.setup()
移动到 environment.py
的顶层。这修复了 AppRegistryNotReady
,但许多情况因
而失败
IntegrityError: duplicate key value violates unique constraint "auth_user_username_key"
DETAIL: Key (username)=(username) already exists.
在 behave-django 下,在每个场景之前启动一个事务,然后回滚;现在似乎不会发生这种情况。 LiveServerTestCase
扩展了 TransactionTestCase
,所以我很困惑。
我很确定错误实际上是在
File "features/steps/common.py", line 5, in <module>
它会在加载应用程序之前尝试加载用户。尝试在 step_impl
.
内移动导入
您似乎误解了 TransactionTestCase
(和 LiveServerTestCase
)与交易相关的内容。它不是使用事务来 运行 你的测试并在最后回滚更改的测试用例(这就是 Django 的 "regular" TestCase
所做的)。这是一个不使用事务的测试用例,您可以测试您自己的事务:
Django’s TestCase class is a more commonly used subclass of TransactionTestCase that makes use of database transaction facilities to speed up the process of resetting the database to a known state at the beginning of each test. A consequence of this, however, is that some database behaviors cannot be tested within a Django TestCase class. For instance, you cannot test that a block of code is executing within a transaction, as is required when using select_for_update(). In those cases, you should use TransactionTestCase.
(source)
TransactionTestCase
仍然通过刷新所有表、触发 post-迁移信号并重新安装固定装置来重置数据库,但它 运行 是在 __call__
的假设下在测试用例上被调用。好像behave是calling run()
directly,所以每次测试都会跳过数据库数据setup/teardown
您可以通过创建自己的子类来解决此问题,该子类在 setUp()
和 tearDown()
中调用 _pre_setup()
和 _post_teardown()
。唯一需要注意的是,每个重写 setUp()
或 tearDown()
的子类必须 调用 super()
或 _pre_setup()
/_post_teardown()
不会被调用。
class BehaveLiveServerTestCase(LiveServerTestCase):
def setUp(self):
self._pre_setup()
def tearDown(self):
self._post_teardown()
您的数据库更改不会在场景之间回滚(因此 IntegrityError
)。数据库仅在 after_all()
中被拆除。尝试将所有代码移动到 before_scenario
和 after_scenario
函数中(参见 related docs)。
测试的执行时间会增加,但测试会被隔离。作为一个快速解决方案,这至少让他们暂时通过了。在评论和备选答案中提示更简洁的解决方案。
这是我最终得到的结果。它允许我的功能在没有 behave-django 的 behave
命令下 运行,因此在 PyCharm 运行 配置中 运行,无需更改功能或步骤. BehaviorDrivenTestCase
和在 before_scenario
中调用 context.test()
是从 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 的 UI 场景。删除 context.test()
并在 before_scenario
的开头添加 management.call_command('flush', verbosity=0, interactive=False)
也可以,需要大约相同的时间,并且应该与异步 Javascript.
一起工作得更好
我有一个 Django (1.10.2) 项目(“theproject”)和一些行为 (0.4.0) 功能。我一直在使用 behave-django。 python manage.py behave
有效。然而,
我整个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
这里是 theproject/setting.py
的 INSTALLED_APPS
以防有用(我为这个实验删除了 '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和behavior集成的方式如何实现?
我尝试过但没有奏效(或不完全)的方法:我在设置 DJANGO_SETTINGS_MODULE
之后将 django.setup()
移动到 environment.py
的顶层。这修复了 AppRegistryNotReady
,但许多情况因
IntegrityError: duplicate key value violates unique constraint "auth_user_username_key"
DETAIL: Key (username)=(username) already exists.
在 behave-django 下,在每个场景之前启动一个事务,然后回滚;现在似乎不会发生这种情况。 LiveServerTestCase
扩展了 TransactionTestCase
,所以我很困惑。
我很确定错误实际上是在
File "features/steps/common.py", line 5, in <module>
它会在加载应用程序之前尝试加载用户。尝试在 step_impl
.
您似乎误解了 TransactionTestCase
(和 LiveServerTestCase
)与交易相关的内容。它不是使用事务来 运行 你的测试并在最后回滚更改的测试用例(这就是 Django 的 "regular" TestCase
所做的)。这是一个不使用事务的测试用例,您可以测试您自己的事务:
Django’s TestCase class is a more commonly used subclass of TransactionTestCase that makes use of database transaction facilities to speed up the process of resetting the database to a known state at the beginning of each test. A consequence of this, however, is that some database behaviors cannot be tested within a Django TestCase class. For instance, you cannot test that a block of code is executing within a transaction, as is required when using select_for_update(). In those cases, you should use TransactionTestCase.
(source)
TransactionTestCase
仍然通过刷新所有表、触发 post-迁移信号并重新安装固定装置来重置数据库,但它 运行 是在 __call__
的假设下在测试用例上被调用。好像behave是calling run()
directly,所以每次测试都会跳过数据库数据setup/teardown
您可以通过创建自己的子类来解决此问题,该子类在 setUp()
和 tearDown()
中调用 _pre_setup()
和 _post_teardown()
。唯一需要注意的是,每个重写 setUp()
或 tearDown()
的子类必须 调用 super()
或 _pre_setup()
/_post_teardown()
不会被调用。
class BehaveLiveServerTestCase(LiveServerTestCase):
def setUp(self):
self._pre_setup()
def tearDown(self):
self._post_teardown()
您的数据库更改不会在场景之间回滚(因此 IntegrityError
)。数据库仅在 after_all()
中被拆除。尝试将所有代码移动到 before_scenario
和 after_scenario
函数中(参见 related docs)。
测试的执行时间会增加,但测试会被隔离。作为一个快速解决方案,这至少让他们暂时通过了。在评论和备选答案中提示更简洁的解决方案。
这是我最终得到的结果。它允许我的功能在没有 behave-django 的 behave
命令下 运行,因此在 PyCharm 运行 配置中 运行,无需更改功能或步骤. BehaviorDrivenTestCase
和在 before_scenario
中调用 context.test()
是从 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 的 UI 场景。删除 context.test()
并在 before_scenario
的开头添加 management.call_command('flush', verbosity=0, interactive=False)
也可以,需要大约相同的时间,并且应该与异步 Javascript.