Why Isn't Behave's Documented Manual Integration With Django Working?
Solution 1:
Your database changes won't be rolled back between scenarios (hence the IntegrityError). The database is only torn down in after_all(). Try moving all code into the before_scenario and after_scenario functions (see related docs).
The execution time of your tests will increase, but the tests will be isolated. And as a quick fix this makes them pass for now at least. Hints for cleaner solutions in the comments and alternative answers.
Solution 2:
You seem to have misunderstood what TransactionTestCase (and LiveServerTestCase) does with relation to transactions. It's not a test case that uses transactions to run your tests and roll back changes at the end (this is what Django's "regular" TestCase does). It's a test case that does not use transactions to you can test your own transactions:
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 still resets the database by flushing all tables, firing the post-migrate signal and reinstalling fixtures, but it runs under the assumption that __call__ is called on the test case. It seems that behave is calling run() directly, so it skips the setup/teardown of database data for each test.
You can probably fix this by creating your own subclass that calls _pre_setup() and _post_teardown() in setUp() and tearDown(). The only caveat is that every subclass that overrides setUp() or tearDown() must call super(), or _pre_setup()/_post_teardown() won't be called.
class BehaveLiveServerTestCase(LiveServerTestCase):
def setUp(self):
self._pre_setup()
def tearDown(self):
self._post_teardown()
Solution 3:
I am pretty sure that the error is actually at
File "features/steps/common.py", line 5, in <module>
Which tries to load the User before loading the apps. Try to move import inside step_impl.
Solution 4:
Here's what I ended up with. It allows my features to run under the behave command without behave-django, and thus to run in PyCharm run configurations, without changing the features or steps. BehaviorDrivenTestCase and calling context.test() in before_scenario are copied from behave-django internals. I'm omitting browser setup and teardown and some other bits not relevant to this question.
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()
Isolating scenarios in transactions won't work for scenarios that exercise a UI that uses async Javascript. Removing context.test() and adding management.call_command('flush', verbosity=0, interactive=False) at the beginning of before_scenario also works, takes about the same amount of time, and should work better with async Javascript.
Post a Comment for "Why Isn't Behave's Documented Manual Integration With Django Working?"