I am writing a Django test inheriting from django.test.TestCase. Everywhere, in the docs, this tutorial and even in this accepted SO accepted answer is stated that when using Django TestCase, a test db will be automatically created.
Previously I have worked with DRF APITestCases and all worked well. Here I am using the very standard approach but the setUpTestData class method is using my production db.
What do I do wrong and what has to be done so a test db is spawned and used for the test?
Please see my code bellow.
from django.test import TestCase
from agregator.models import AgregatorProduct
from django.db.models import signals
def sample_product_one():
sample_product_one = {
# "id": 1,
"name": "testProdOne",
"dph": 21,
"updated": datetime.now(),
"active": True,
"updatedinstore": False,
"imagechanged": False,
"isVirtualProduct": False,
}
return sample_product_one
class TestCreateProdToCategory(TestCase):
"""
Test for correct creation of records
"""
#classmethod
#factory.django.mute_signals(signals.pre_save, signals.post_save)
def setUpTestData(cls):
AgregatorProduct.objects.create(
**sample_product_one()
)
def test_create_prod_to_cat(self):
product = AgregatorProduct.objects.get(id=1)
self.assertEqual(product.id, 1)
DB set up:
DATABASES = {
'agregator': {
'NAME': 'name',
'ENGINE': 'sql_server.pyodbc',
'HOST': 'my_ip',
'USER': 'my_user',
'PASSWORD': 'my_pwd',
'OPTIONS': {
'driver': 'ODBC Driver 17 for SQL Server',
'isolation_level': 'READ UNCOMMITTED',
},
}
}
The test results in
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\xevos\xevosadmin\agregator\tests\test_admin_actions\test_products_to_categories_admin_action.py", line 64, in test_create_prod_to_cat
product = AgregatorProduct.objects.get(id=1)
File "C:\xevos\xevosadmin\.venv\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\xevos\xevosadmin\.venv\lib\site-packages\django\db\models\query.py", line 397, in get
raise self.model.DoesNotExist(
agregator.models.AgregatorProduct.DoesNotExist: AgregatorProduct matching query does not exist.
----------------------------------------------------------------------
which is a result of id being autoincrementing and given there are products in the production db already, it gets id of eg 151545
(AgregatorProduct matching query does not exist. is a result of the fact that the product which used to have id=1 was deleted a long time ago in the production db.)
So the test writes to the existing database and the data persist there even after the test is finished.
To create a test database, use the setUp method inside TestCase and run it using python manage.py test
from django.test import TestCase
from myapp.models import Animal
class AnimalTestCase(TestCase):
def setUp(self):
Animal.objects.create(name="lion", sound="roar")
Animal.objects.create(name="cat", sound="meow")
def test_animals_can_speak(self):
"""Animals that can speak are correctly identified"""
lion = Animal.objects.get(name="lion")
cat = Animal.objects.get(name="cat")
self.assertEqual(lion.speak(), 'The lion says "roar"')
self.assertEqual(cat.speak(), 'The cat says "meow"')
The database will be created and deleted automatically after tests are done
https://docs.djangoproject.com/en/3.1/topics/testing/overview/#writing-tests
Related
I want to test register view in django project,
so I build some fake test cases(self.correct_samples)
after register successfully, it should redirect to home page which means the status code should be 302.
from django.test import TestCase
from django.urls.base import reverse
class RegisterTests(TestCase):
def setUp(self):
url = reverse('account:register')
self.response = self.client.get(url)
self.correct_samples = (
('testuser1#email.com', 'testuser', 'test112233', 'test112233'),
('fakeuser#email.com', 'fake123', 'fakeuser111', 'fakeuser111'),
('correct#email.com', 'Jack', 'myfavorite', 'myfavorite'),
('failemail', 'Jack', 'myfavorite', 'myfavorite'), # fail for purpose
)
def test_register_form(self):
for test_case in self.correct_samples:
email, username, password1, password2 = test_case
self.response = self.client.post(reverse('account:register'), data={
'email': email,
'username': username,
'password1': password1,
'password2': password2,
})
self.assertEqual(self.response.status_code, 302)
self.assertRedirects(
self.response, expected_url='/', status_code=302, target_status_code=200)
The fourth data in self.correct_samples which is ('failemail', 'Jack', 'myfavorite', 'myfavorite') should be a fail case.
but after python manage.py test. It passed.
(env) C:\Users\User\myblog>python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
C:\Users\User\myblog\env\lib\site-packages\whitenoise\base.py:115: UserWarning: No directory at: C:\Users\User\myblog\staticfiles\
warnings.warn(u"No directory at: {}".format(root))
.
----------------------------------------------------------------------
Ran 1 test in 0.195s
OK
Destroying test database for alias 'default'...
Here comes the tricky thing,
it failed after switching order from fourth to first.
from django.test import TestCase
from django.urls.base import reverse
class RegisterTests(TestCase):
def setUp(self):
...
self.correct_samples = (
('failemail', 'Jack', 'myfavorite', 'myfavorite'), # fail for purpose
('testuser1#email.com', 'testuser', 'test112233', 'test112233'),
('fakeuser#email.com', 'fake123', 'fakeuser111', 'fakeuser111'),
('correct#email.com', 'Jack', 'myfavorite', 'myfavorite'),
)
def test_register_form(self):
...
result:
(env) C:\Users\User\myblog>python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
C:\Users\User\myblog\env\lib\site-packages\whitenoise\base.py:115: UserWarning: No directory at: C:\Users\User\myblog\staticfiles\
warnings.warn(u"No directory at: {}".format(root))
F
======================================================================
FAIL: test_register_form (account.tests.RegisterTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\User\myblog\account\tests.py", line 34, in test_register_form
self.assertEqual(self.response.status_code, 302)
AssertionError: 200 != 302
----------------------------------------------------------------------
Ran 1 test in 0.026s
FAILED (failures=1)
Destroying test database for alias 'default'...
Why this happened?
I have searched for related keywords like unittest in forloop, multiple testcases in forloop.
but it seems no answers for it, or maybe search through other keywords?
or something I missed or misunderstood?
thanks for helping.
Problem has been solved!
According to this questions How do you generate dynamic (parameterized) unit tests in Python?
Solution: parameterized
I am trying to run pytest and am getting this error:
RuntimeError: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.
I have the following test in test_models.py that checks if the uuid has been added to the User (it is on autoadd):
import pytest
from backendapps.core.models import User
pytestmark = pytest.mark.django_db
class TestUserModel():
user = User.objects.create()
assert user.uuid is not None
I also have a fixtures file called conftest.py at the root level.
import pytest
from backendapps.core.models import Org, User
#pytest.fixture(autouse=True)
def enable_db_access(db):
pass
I then also have this pytest.ini also at the root level:
[pytest]
testpaths = backendapps
addopts = --ds=config.settings.local --reuse-db --create-db
python_files = tests.py test_*.py *_tests.py```
My test database is set to:
'TEST': {
'NAME': 'project_test_db',
},
Going through other posts, here are the debug steps I have taken:
add the pytestmark = pytest.mark.django_db line - I have this
Check the db permissions - my user has superuser permissions
Check for migration errors on your db - I migrated all my migrations to zero and reran them to check if I had any manual settings when I migrated and it was all fine.
Any thoughts on what to try or how to get a clearer error?
Full error:
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ERROR collecting backendapps/core/tests/test_models.py ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
backendapps/core/tests/test_models.py:18: in <module>
class TestUserModel():
backendapps/core/tests/test_models.py:27: in TestUserModel
user = User.objects.create()
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/db/models/manager.py:85: in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/db/models/query.py:447: in create
obj.save(force_insert=True, using=self.db)
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/db/models/base.py:753: in save
self.save_base(using=using, force_insert=force_insert,
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/db/models/base.py:790: in save_base
updated = self._save_table(
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/db/models/base.py:895: in _save_table
results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/db/models/base.py:933: in _do_insert
return manager._insert(
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/db/models/manager.py:85: in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/db/models/query.py:1249: in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/db/models/sql/compiler.py:1395: in execute_sql
with self.connection.cursor() as cursor:
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/utils/asyncio.py:26: in inner
return func(*args, **kwargs)
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/db/backends/base/base.py:259: in cursor
return self._cursor()
../../.local/share/virtualenvs/projectAPI-NVQT3lgx/lib/python3.8/site-packages/django/db/backends/base/base.py:235: in _cursor
self.ensure_connection()
E RuntimeError: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.
================================================================================= short test summary info ==================================================================================
FAILED backendapps/core/tests/test_models.py - RuntimeError: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Results (0.49s):
I guess this error is due to the fact that you're trying to create the User on the test object directly. Therefore, the code will be executed before the Database is setup and hence the error.
You might try to create the User in a test method:
class TestUserModel:
def test_user_uuid_is_not_none(self):
user = User.objects.create()
assert user.uuid is not None
Or you could simply just run a test function
def test_user_uuid_is_not_none(self):
user = User.objects.create()
assert user.uuid is not None
If you need access to the User several times in your Test, create a fixture and use it in the test:
[conftest.py]
#pytest.fixture
def user() -> settings.AUTH_USER_MODEL:
# return the UserFactory (factoryboy)
return UserFactory()
[test_models.py]
import pytest
from django.contrib.auth import get_user_model
pytestmark = pytest.mark.django_db
User = get_user_model()
class TestUserModel:
def test_user_uuid_is_not_none(self, user: User):
assert user.uuid is not None
From pytest docs:
By default your tests will fail if they try to access the database. Only if you explicitly request database access will this be allowed. This encourages you to keep database-needing tests to a minimum which is a best practice since next-to-no business logic should be requiring the database. Moreover it makes it very clear what code uses the database and catches any mistakes.
The easiest solution would be to add fixture as decorator:
#pytest.mark.django_db(True)
class TestUserModel():
This question already has an answer here:
Django: Detect database backend
(1 answer)
Closed 3 years ago.
I'm hoping this will be similar to a few questions which have been previously answered.
I was wondering what the full processes are for testing the DB connection string (not the DB name) for the current connected DB in Django is? I see a few methods for printing the name, but across local development and staging, these names will be the same. I need to know more, such as the db engine (e.g., is it PostgreSQL, or is it db.sqlite3. etc etc), and various other DB parameters.
I'm currently trying to debug why a Celery beat task is failing to create database entries for the application DB I think I am connected to.
I'm performing a simple obj, created = Object.objects.get_or_create() DB method, and on logging created, some are True and some are False. All good so far.
However, the admin section of the Django CMS is not showing any more entries to the DB.
I believe testing the location and connection strings of the DB the Django application is using might be useful in debugging this ghost object creation...unless someone can advise on similar issues they have had with the Celery daemon not actually persisting a DB create/update etc...
Perhaps, something such as:
from django.db import connection
print(connection.settings_dict)
Would be sufficient?
So, the following was useful for determining the DB engine:
from django.db import connection
print(connection.settings_dict)
For useful guidance, the connection.settings_dict contains the following structure:
{
'ENGINE': 'django.db.backends.sqlite3',
'NAME': '/usr/src/app/db.sqlite3',
'ATOMIC_REQUESTS': False,
'AUTOCOMMIT': True,
'CONN_MAX_AGE': 0,
'OPTIONS': {},
'TIME_ZONE': None,
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
'TEST': {
'CHARSET': None,
'COLLATION': None,
'NAME': None,
'MIRROR': None
}
}
So, we could work with something like this in a management command:
from django.db import connection
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = 'Displays The Currently Connected DB Settings'
def handle(self, *args, **kwargs):
print(
"The current database engine is {ENGINE}".format(**connection.settings_dict)
)
print(
"Currently connected to host {HOST} on port {PORT} ".format(**connection.settings_dict)
)
I'm setting cassandra engine in django project, I successfully run server but when I click model of myapp in admin site I got error:
Settings' object has no attribute 'CASSANDRA_FALLBACK_ORDER_BY_PYTHON
I first sync cassandra then migrate it,after that i runserver there are no error but in admin site when i want to put data in my model got this error
For database I use django_cassandra_engine
DATABASES = {
'default': {
'ENGINE': 'django_cassandra_engine',
'NAME': 'db',
'TEST_NAME': 'Test Cluster',
'HOST': '127.0.0.1 ',
'POST': '9042',
'OPTIONS': {
'replication': {
'strategy_class': 'SimpleStrategy',
'replication_factor': 1
},
'connection': {
'consistency': ConsistencyLevel.LOCAL_ONE,
'retry_connect': True
# + All connection options for cassandra.cluster.Cluster()
},
'session': {
'default_timeout': 10,
'default_fetch_size': 10000
# + All options for cassandra.cluster.Session()
}
}
}
And I have model like this:
from cassandra.cqlengine import columns
from django_cassandra_engine.models import DjangoCassandraModel
class mymodel(DjangoCassandraModel):
name = columns.Text(required=True)
phone = columns.Integer(primary_key=True)
in my terminal i get
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/django_cassandra_engine/models/__init__.py", line 745, in order_by
*self._get_ordering_condition(col))
File "/usr/local/lib/python3.7/dist-packages/cassandra/cqlengine/query.py", line 1133, in _get_ordering_condition
raise QueryException("Can't resolve the column name: '{0}'".format(colname))
cassandra.cqlengine.query.QueryException: Can't resolve the column name: 'pk'
File "/usr/local/lib/python3.7/dist-packages/django/conf/__init__.py", line 80, in __getattr__
val = getattr(self._wrapped, name)
AttributeError: 'Settings' object has no attribute 'CASSANDRA_FALLBACK_ORDER_BY_PYTHON'
I also use rest_framework for api view
How to solve this type of problem
Add the following to your settings:
CASSANDRA_FALLBACK_ORDER_BY_PYTHON = True
I'm trying to use my fixtures in a UnitTest.
AddFavoritesTestCase(unittest.TestCase):
fixtures = ['/Users/Bryan/work/osqa/fixtures/fixture_questions.json']
def setUp(self):
self.factory = RequestFactory()
def testAdminCanFavorite(self):
user = User.objects.get(pk=3)
...
self.assertEqual(response.status_code, 200)
======================================================================
ERROR: testAdminCanFavorite (forum.tests.tests_building_stickyness.AddFavoritesTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/Bryan/work/osqa/forum/tests/tests_building_stickyness.py", line 18, in testAdminCanFavorite
user = User.objects.get(pk=3) # Kallie has admin
File "/usr/local/lib/python2.7/site-packages/Django-1.3-py2.7.egg/django/db/models/manager.py", line 132, in get
return self.get_query_set().get(*args, **kwargs)
File "/Users/Bryan/work/osqa/forum/models/base.py", line 64, in get
obj = super(CachedQuerySet, self).get(*args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/Django-1.3-py2.7.egg/django/db/models/query.py", line 349, in get
% self.model._meta.object_name)
DoesNotExist: User matching query does not exist.
It seems the fixtures are not loading.
I've been able to use the fixtures to populate the database, but for some reason the fixtures aren't being found in the tests.
The path is correct but I can't figure out what's going wrong.
$ ls /Users/Bryan/work/osqa/fixtures/fixture_questions.json
/Users/Bryan/work/osqa/fixtures/fixture_questions.json
Running the test at a higher verbosity shows that the fixtures are not being found.
I'm running Django 1.3.
Import the TestCase from django.test;
Not: import unittest
Not: import django.utils.unittest
But: import django.test
Like this:
from django.test import TestCase
class test_something(TestCase):
fixtures = ['one.json', 'two.json']
...
https://docs.djangoproject.com/en/1.3/topics/testing/#django.test.TestCase
You don't pass the full path to the fixture, just the fixture name:
fixtures = ['fixture_questions.json']
As long as the fixture is in a fixtures directory within an app that's in INSTALLED_APPS, Django will find it.