我正在使用带有selenium的萝卜bdd来测试我的django应用程序,但有时django要求删除数据库,因为它已经存在于数据库中。这是我的terrain.py
:
import os
import django
from django.test.runner import DiscoverRunner
from django.test import LiveServerTestCase
from radish import before, after
from selenium import webdriver
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tangorblog.settings.features')
BASE_URL = os.environ.get('BASE_URL', 'http://localhost:8000')
@before.each_scenario
def setup_django_test(scenario):
django.setup()
scenario.context.test_runner = DiscoverRunner()
scenario.context.test_runner.setup_test_environment()
scenario.context.old_db_config =\
scenario.context.test_runner.setup_databases()
scenario.context.base_url = BASE_URL
scenario.context.test_case = LiveServerTestCase()
scenario.context.test_case.setUpClass()
scenario.context.browser = webdriver.Chrome()
@after.each_scenario
def teardown_django(scenario):
scenario.context.browser.quit()
scenario.context.test_case.tearDownClass()
del scenario.context.test_case
scenario.context.test_runner.teardown_databases(
scenario.context.old_db_config)
scenario.context.test_runner.teardown_test_environment()
我认为,我可以在某种程度上改变这一点
scenario.context.old_db_config =\
scenario.context.test_runner.setup_databases()
但我不知道怎么做。有什么帮助吗?
答案 0 :(得分:1)
RadishTestRunner
,因为这个想法来自你。我修改了它,所以我可以django unittest
单独运行radish
。我直接使用LiveServerTestCase
删除LiveServer
,因为我注意到两者之间的相似性,但LiveServerTestCase
继承自TransactionTestCase
并且内置了LiveServerThread
和_StaticFilesHandler
。这里&# 39;它是怎么回事:
# package/test/runner.py
import os
from django.test.runner import DiscoverRunner
import radish.main
class RadishTestRunner(DiscoverRunner):
radish_features = ['features']
def run_suite(self, suite, **kwargs):
# run radish test
return radish.main.main(self.radish_features)
def suite_result(self, suite, result, **kwargs):
return result
def set_radish_features(self, features):
self.radish_features = features
# radish/world.py
from django.test import LiveServerTestCase
from radish import pick
from selenium import webdriver
@pick
def get_browser():
return webdriver.Chrome()
@pick
def get_live_server():
live_server = LiveServerTestCase
live_server.setUpClass()
return live_server
# radish/terrain.py
from radish import world, before, after
from selenium import webdriver
@before.all
def set_up(features, marker):
world.get_live_server()
@after.all
def tear_down(features, marker):
live_server = world.get_live_server()
live_server.tearDownClass()
@before.each_scenario
def set_up_scenario(scenario):
live_server = world.get_live_server()
scenario.context.browser = webdriver.Chrome()
scenario.context.base_url = live_server.live_server_url
scenario.context.test_case = live_server()
scenario.context.test_case._pre_setup()
@after.each_scenario
def tear_down_scenario(scenario):
scenario.context.test_case._post_teardown()
scenario.context.browser.quit()
那就是它。这也解决了PostgreSQL在我指出的另一个问题上的问题。我还打开并退出每个场景的浏览器,因为它让我可以更好地控制场景中的浏览器。非常感谢你努力指出我正确的方向。
最后我回到PostgreSQL。 在速度方面,PostgreSQL似乎比MySQL更好。它大大减少了运行测试的时间。
哦,是的,我需要在./manage.py collectstatic
设置文件中指定STATIC_ROOT
后先运行django
。
我还修改了RadishTestRunner
,因此我可以使用RADISH_ONLY=1
运行python manage.py radish /path/to/features/file
,而不是# package.management.commands.radish
from __future__ import absolute_import
import sys
from django.core.management.base import BaseCommand, CommandError
from package.test.runner import RadishTestRunner
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('features', nargs='+', type=str)
def handle(self, *args, **options):
test_runner = RadishTestRunner(interactive=False)
if options['features']:
test_runner.set_radish_features(options['features'])
result = test_runner.run_suite(None)
if result:
sys.exit(result)
。这是我的萝卜命令:
var str = '(a (b) c) d e (f g) h';
var match;
var myRe = /\([^]+?\)|\S+/g;
var result = [];
while (match = myRe.exec(str)) {
result.push(match[0]);
}
var tmp = "";
var final = [];
for (var i = 0; i < result.length; i++) {
var leftP = (result[i].match(/\(/g) || []).length;
var rightP = (result[i].match(/\)/g) || []).length;
if (leftP !== rightP) {
tmp += result[i];
for (var j = i + 1; j < result.length; j++) {
tmp += result[j];
if ((tmp.match(/\(/g) || []).length === (tmp.match(/\)/g) || []).length) {
final.push(tmp);
tmp = "";
i = j + 1;
break;
}
}
} else {
final.push(result[i]);
}
}
for (var i = 0; i < final.length; i++) {
final[i] = final[i].replace(/\)(\S+)/g, ') $1');
}
for (var i = 0; i < final.length; i++) {
final[i] = final[i].replace(/^\(([^]+)\)$/, '$1');
}
通过使用萝卜与django管理命令,我们可以控制我们想要运行的功能文件。
答案 1 :(得分:1)
在我看来,为每个场景重新创建数据库最终会导致效率极低(并且速度极慢)。应该只需要在每次测试运行时创建一次数据库,然后将其放在最后。
我想出了一个我认为与Django更好地集成的解决方案。它允许您使用manage.py test
运行测试,每次测试运行只创建/删除数据库一次,并在测试每个功能后清除数据库表。
请注意,默认情况下,它会同时运行Django单元测试和萝卜测试。要只运行萝卜测试,你可以RADISH_ONLY=1 manage.py test
。此外,要使实时服务器/ Selenium测试起作用,您必须先运行manage.py collectstatic
。
# package/settings.py
TEST_RUNNER = 'package.test.runner.RadishTestRunner'
# package/test/runner
import os
from django.test.runner import DiscoverRunner
import radish.main
class RadishTestRunner(DiscoverRunner):
def run_suite(self, suite, **kwargs):
# Run unit tests
if os.getenv('RADISH_ONLY') == '1':
result = None
else:
result = super().run_suite(suite, **kwargs)
# Run radish behavioral tests
self._radish_result = radish.main.main(['features'])
return result
def suite_result(self, suite, result, **kwargs):
if result is not None:
# Django unit tests were run
result = super().suite_result(suite, result, **kwargs)
else:
result = 0
result += self._radish_result
return result
# radish/world.py
from django.db import connections
from django.test.testcases import LiveServerThread, _StaticFilesHandler
from django.test.utils import modify_settings
from radish import pick
from selenium import webdriver
@pick
def get_browser():
return webdriver.Chrome()
@pick
def get_live_server():
live_server = LiveServer()
live_server.start()
return live_server
class LiveServer:
host = 'localhost'
port = 0
server_thread_class = LiveServerThread
static_handler = _StaticFilesHandler
def __init__(self):
connections_override = {}
for conn in connections.all():
if conn.vendor == 'sqlite' and conn.is_in_memory_db():
conn.allow_thread_sharing = True
connections_override[conn.alias] = conn
self.modified_settings = modify_settings(ALLOWED_HOSTS={'append': self.host})
self.server_thread = self.server_thread_class(
self.host,
self.static_handler,
connections_override=connections_override,
port=self.port,
)
self.server_thread.daemon = True
@property
def url(self):
self.server_thread.is_ready.wait()
return 'http://{self.host}:{self.server_thread.port}'.format(self=self)
def start(self):
self.modified_settings.enable()
self.server_thread.start()
self.server_thread.is_ready.wait()
if self.server_thread.error:
self.stop()
raise self.server_thread.error
def stop(self):
if hasattr(self, 'server_thread'):
self.server_thread.terminate()
for conn in connections.all():
if conn.vendor == 'sqlite' and conn.is_in_memory_db():
conn.allow_thread_sharing = False
self.modified_settings.disable()
# radish/terrain.py
from django.db import connections, transaction
from radish import world, before, after
@before.all
def set_up(features, marker):
world.get_live_server()
@after.all
def tear_down(features, marker):
browser = world.get_browser()
live_server = world.get_live_server()
browser.quit()
live_server.stop()
@before.each_scenario
def set_up_scenario(scenario):
live_server = world.get_live_server()
scenario.context.base_url = live_server.url
scenario.context.browser = world.get_browser()
# XXX: Only works with the default database
# XXX: Assumes the default database supports transactions
scenario.context.transaction = transaction.atomic(using='default')
scenario.context.transaction.__enter__()
@after.each_scenario
def tear_down_scenario(scenario):
transaction.set_rollback(True, using='default')
scenario.context.transaction.__exit__(None, None, None)
for connection in connections.all():
connection.close()