I have a very simple test as follows:
import models
from django.test import TestCase
MyViewTest(TestCase):
def setUp(self):
self.trip = models.Trip.objects.order_by('?')[0]
def test_something(self):
# Blah Blah
whenever i run test it throws the error mentioned below:
Traceback (most recent call last):
File "/home/amyth/Projects/test/trips/tests.py", line 8, in setUp
self.trip = models.Trip.objects.order_by('?')[0]
File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 207, in __getitem__
return list(qs)[0]
IndexError: list index out of range
I also tried changing the query to models.Trip.objects.all()[0] and it still throws the same error. What's strange is if I use any of the above queries within the shell it works. Then howcome it is not working within a test ?
See the documentation on testing in django. A new 'test' database is created, and your 'production' database is not used. Unless you create Trip entries in the TestCase setUp method, it is empty. Also, after each TestCase is run, the database is truncated, so if you need to use the Trips in multiple TestCases, you will need to create the database entry for it in each TestCase setUp.
Related
Am trying to load some generated data into Django without disrupting the existing data in the site. What I have:
Saved the data as a valid JSON (validated here).
The JSON format matches the Django documentation. In previous attempts I also aligned it to the Django documentation here (slightly different field order, the result was the same).
Output errors I'm receiving are very generic and not helpful, even with verbosity=3.
The Error Prompt
Operations to perform:
Apply all migrations: workoutprogrammes
Running migrations:
Applying workoutprogrammes.0005_auto_20220415_2021...Loading '/Users/Robert/Desktop/Projects/Powerlifts/src/workoutprogrammes/fixtures/datafile_2' fixtures...
Checking '/Users/Robert/Desktop/Projects/Powerlifts/src/workoutprogrammes/fixtures' for fixtures...
Installing json fixture 'datafile_2' from '/Users/Robert/Desktop/Projects/Powerlifts/src/workoutprogrammes/fixtures'.
Traceback (most recent call last):
File "/Users/Robert/Desktop/Projects/Powerlifts/venv/lib/python3.8/site-packages/django/core/serializers/json.py", line 70, in Deserializer
yield from PythonDeserializer(objects, **options)
File "/Users/Robert/Desktop/Projects/Powerlifts/venv/lib/python3.8/site-packages/django/core/serializers/python.py", line 93, in Deserializer
Model = _get_model(d["model"])
KeyError: 'model'
The above exception was the direct cause of the following exception:... text continues on...
for obj in objects:
File "/Users/Robert/Desktop/Projects/Powerlifts/venv/lib/python3.8/site-packages/django/core/serializers/json.py", line 74, in Deserializer
raise DeserializationError() from exc
django.core.serializers.base.DeserializationError: Problem installing fixture '/Users/Robert/Desktop/Projects/Powerlifts/src/workoutprogrammes/fixtures/datafile_2.json':
auto_2022_migration.py file:
from django.db import migrations
from django.core.management import call_command
def db_migration(apps, schema_editor):
call_command('loaddata', '/filename.json', verbosity=3)
class Migration(migrations.Migration):
dependencies = [('workoutprogrammes', '0004_extable_delete_ex_table'),]
operations = [migrations.RunPython(db_migration),]
JSON file extract (start... end)
NB: all my PKs are UUIDs generated from postgresql
[{"pk":"af82d5f4-2814-4d52-b2b1-6add6cf18d3c","model":"workoutprogrammes.ex_table","fields":{"exercise_name":"Cable Alternating Front Raise","utility":"Auxiliary","mechanics":"Isolated","force":"Push","levator_scapulae":"Stabilisers",..."obliques":"Stabilisers","psoas_major":""}}]
I'm not sure what the original error was. I suspect it had to do with either editing the table/schema using psql separately from Django, OR using the somewhat outdated uuid-ossp package instead of pgcrypto package (native to Djagno via CryptoExtension()). I was never able to confirm. I did however manage to get it working by:
Deleting the table (schema, data) using psql and the app using Django. Creating a new app in Django.
Load the prev model data into this new_app/models.py. Make new migration file, first updating the CryptoExtension() operation before committing the migration.
Create Fixtures directory and relevant file (per Django specs, validated JSON online.
Validate my UUIDs online
Load the fixture data into the app. python3 manage.py loaddata /path/to/data/file_name.json
I have found a strange bug in my Django application. I have a view accessible on /people/<pk>. In tests it works just fine, but in production I have stumbled over a bug at several (just a few!) pk's: Reverse for 'single-person' with arguments '('',)' not found. 1 pattern(s) tried: ['people/(\\d+)/?$']. Since I couldn't catch it with my tests, I'd like to create another test, with the current state of the database, to debug. Also, to prevent future situations like this, I'd always like to be able to run my tests with a copy of the production database, to minimize the chances something goes wrong with the actual data.
I thought it would be as straightforward as
manage.py dumdata --output db-test.json
and then
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.test import Client
from people.models import Person
class TestPeopleWithMyData(StaticLiveServerTestCase):
fixtures = ['db-test.json']
def setUp(self):
self.client = Client()
self.client.force_login(user='test_user')
def test_person(self):
for person in Person.objects.all():
print('Testing %s: ' % person, end='')
r = self.client.get('/people/%d' % person.pk)
print(r)
...
However this attempt fails:
======================================================================
ERROR: setUpClass (people.tests.test_my_data.TestPeopleWithMyData)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/username/Documents/python_projects/project/venv/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py", line 178, in __get__
rel_obj = getattr(instance, self.cache_name)
AttributeError: 'Person' object has no attribute '_added_by_cache'
(I have a field named added_by in my Person model.) And then it goes on to say django.contrib.auth.models.DoesNotExist: Problem installing fixture '/Users/username/Documents/python_projects/project/db-test.json': User matching query does not exist.
Since I know that this exactly database works just fine, it doesn't look right to me. So before I dive into debugging this problem, I would like to understand if what I am doing is maybe fundamentally wrong and I shouldn't be able to create test fixtures just like that. Or am I missing some simple mistake?
I think you need to do before running test
python manage.py makemigrations
python manage.py migrate
In your fixture file you have '_added_by_cache' that's not in your model.
I have a django_rest_framework test (the problem is the same with a regular django test) that looks like this:
from rest_framework.test import APITestCase
class APITests(APITestCase):
# tests for unauthorized access
def test_unauthorized(self):
...
for api in apipoints:
response = self.client.options(api)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
I have a url that fails, the terminal shows this:
FAIL: test_unauthorized (app.misuper.tests.APITests)
---------------------------------------------------------------------- Traceback (most recent call last): File
"/home/alejandro/...",
line 64, in test_unauthorized
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) AssertionError: 200 != 403
Ok, how can I know which url failed the test? I am iterating through all urls that require login, that is many urls, how can I print the one that failed the test?
For a simple quick-fix, you can pass the apipoint in the third parameter of the assertion method:
>>> from unittest import TestCase
>>> TestCase('__init__').assertEqual(1, 2, msg='teh thing is b0rked')
AssertionError: teh thing is b0rked
In the spirit of unit testing, these should really be each different test methods rather than only one test method with the loop. Check out nose_parameterized for help with making that more DRY. You'll decorate the test method like this:
from nose_parameterized import parameterized
#parameterized.expand(apipoints)
def test_unauthorized(self, apipoint):
response = self.client.options(apipoint)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
The decorator will generate different test methods for each endpoint, so that they can pass/fail independently of one another.
Although this package has nose in the name, it's also compatible with other runners such as unittest and py.test.
It looks like whenever I use transaction.set_autocommit(False) in a test case, I get the following stack trace:
transaction.set_autocommit(False)
File "/Users/btoueg/src/python/python3.3.3_django1.6.1/lib/python3.3/site-packages/django/db/transaction.py", line 133, in set_autocommit
return get_connection(using).set_autocommit(autocommit)
File "/Users/btoueg/src/python/python3.3.3_django1.6.1/lib/python3.3/site-packages/django/db/backends/__init__.py", line 331, in set_autocommit
self.validate_no_atomic_block()
File "/Users/btoueg/src/python/python3.3.3_django1.6.1/lib/python3.3/site-packages/django/db/backends/__init__.py", line 360, in validate_no_atomic_block
"This is forbidden when an 'atomic' block is active.")
django.db.transaction.TransactionManagementError: This is forbidden when an 'atomic' block is active.
Is this normal behavior ? It looks like Django’s TestCase class wraps each test in a transaction for performance reasons.
So the question is : how do I test my code in a Django Testcase if it already uses a transaction ?
I'm using Django 1.6 with PostgreSQL 9.2
Django TestCase inherits from TransactionTestCase.
According to the doc, TestCase does basically the same as TransactionTestCase, but surrounds every test with a transaction (...). You have to use TransactionTestCase, if you need transaction management inside a test.
My situation is a little bit different because my test class is derived from DRF APITestCase. So in order to check transaction management in my test case, I did the following:
from rest_framework.test import APITestCase
from django.test import TestCase
class MyTestCase(APITestCase):
def _fixture_setup(self):
super(TestCase, self)._fixture_setup()
def _fixture_teardown(self):
super(TestCase, self)._fixture_teardown()
...
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.