django changes in tests.py don't reflect in models.py - django

I have a recurring problem when testing my app. whenever I change or create() any object from within tests.py, these changes can't be found in models.py - and that happens in same test.
pseudocode:
tests.py:
def test_something(self):
...
Norm.objects.create(...)
self.player_a.print_all_norms()
...
models.py:
def print_all_norms():
a = Norm.objects.all()
print a
# prints [], the Norm object created in tests.py wasn't found
return
EDIT:
Clarification - I can't find the object within the test that created it.
A Norm object is created inside test_something(), which calls a function inside models.py.
When the function tries to find the previously created object using Norm.objects.all(), it fails, the test resumes, and then test fails as well.

Testing uses temporary database as documented in the test database docs, so after the test is complete, you won't be able to find those objects through the model manager.
Is it not finding the object within the test or when you try to find it after executing the test?
If it's not finding it in the test, try making sure you have the proper permissions (as mentioned in test db docs)
If you want to load predetermined values into the database on some sort of consistent basis, outside of testing, you may want to at using fixtures

Related

django test - how to avoid the ForeignKeyViolation

I am struggling to create tests with django with objects that have foreign keys. The tests run with no errors when run individually, but when running all, i get ForeignKeyViolation in the teardown phase (which I don't understand what it does and can't find any info on it).
When starting the testsuite I am creating a bunch of dummy data like this:
def setUp(self) -> None:
create_bulk_users(5)
create_categories()
create_tags()
create_technologies()
self.tags = Tag.objects.all()
self.categories = Category.objects.all()
self.technologies = Technology.objects.all()
The problems I need help figuring out are:
What exactly the teardown phase does? Are there any detailed docs on it?
How should I structure my tests so to avoid the ForeignKeyViolation issue?
What exactly the teardown phase does? Are there any detailed docs on it?
I couldn't find any detailed docs about it. All I find out was by reading the actual code. Teardown cancels any operations done to the DB during the tests.
The way TestCases work (broadly) is this:
1. SetUp method runs **before** every test
2. individual test method is run
3. TearDown method runs **after** every test
4. Test database is flushed
How should I structure my tests so to avoid the ForeignKeyViolation issue?
My mistake was that I was importing a function for creating dummy data.
The imported module was running a function to populate some primary data. The problem was that was not rerun each within the setUp method because it would run just one, when importing the file.
# imported module
# will delete all tags after running first test but
# the reference to the old tags will persist
tags = FakeTags.create_bulk();
def create_bulk_articles():
article = FakeArticle.create()
article.tags.add(tags)
I fixed it by just using the trouble function inside the imported function.
# imported module
# tags are created each time before running the tests
def create_bulk_articles():
tags = FakeTags.create_bulk();
article = FakeArticle.create()
article.tags.add(tags)

How to load fixtures for a LiveServerTestCase

I am attempting to write test cases in Django using Selenium. I want to use existent fixtures so my test database (SQLite3) has test data for every test.
I have some model test cases (just using the TestCase class) as follows;
from django.test import TestCase
from django.test import LiveServerTestCases
from missions.models import Mission, MissionDataRecord
class MissionModelTests(TestCase):
fixtures = ['myproject_common/fixtures/auth_initial_load.json', 'worklog/fixtures/worklogs_initial_load',
'missions_initial_load.json']
def test_object_name_is_mission_title(self):
mission = Mission.objects.get(id=1)
self.assertEqual(mission.title, str(mission))
def test_object_name_is_mission_title_again(self):
mission = Mission.objects.get(id=1)
self.assertEqual(mission.title, str(mission))
This works as expected when run like this (I get two test passes). However, for Selenium testing I need to use LiveServerTestCase instead of TestCase.
The simple example above is a model test, but for illustrative purposes of the issue I'm facing with Selenium, if I simply replace "TestCase" with "LiveServerTestCase" the first test passes, but the second test fails, with the error
django.db.utils.IntegrityError: Problem installing fixture
'[...]/fixtures/auth_initial_load.json': Could not load
auth.User(pk=1): UNIQUE constraint failed: auth_user.username
This error occurs in the _fixture_setup of /django/test/testcases.py. This seems to suggests that my fixtures (specifically the auth_initial_load fixture) is attempting to load again ON TOP of existing data. However, from django docs reading this should not be taking place, because each test should run in its own transaction (which I believe means the fixtures are loaded for each transaction).
What is going on here, and more importantly, how can I use LiveServerTestCase with my existing fixtures (similar to how I am using TestCase currently)? In reality I need to use StaticLiveServerTestCase, but Im guessing the code will be the same.
It turns out the way I was loading fixtures correctly after all. The issue was with my fixtures themselves, using hard coded primary (and foreign) keys. In my case two users were being created before the fixtures were loaded, so when fixtures tried to load with the same primary key, a UNIQUE constraint violation occurred. The solution was to re-generate my fixtures using the --natural-primary and --natural-foreign flags as suggested in this SO answer.

Django unit testing: Separating unit tests without querying the database multiple times

I have a pair of tests like this:
Make sure a task's deletion status initializes as None
def test_initial_task_deletion_status_is_none(self):
unfinished_task = Task.objects.get(tasked_with="Unfinished Test")
self.assertIsNone(unfinished_task.delete_status)
# Make sure a task's deletion status changes appropriately
def test_unfinished_task_deletion_status_updates_appropriately(self):
unfinished_task = Task.objects.get(tasked_with="Unfinished Test")
unfinished_task.timed_delete(delta=.1)
self.assertIs(unfinished_task.delete_status, "Marked for Deletion")
This will go on, but I'll have unfinished_task = Task.objects.get(tasked_with="Unfinished Test") at the beginning of every one. Is there a way to split these types of things into separate tests, but use the same query result?
Assuming you're using Django's testing framework, then you can do this using setUp().
More about unittest.TestCase.setUp() here
So your updated snippet would look like:
from django.test import TestCase
class MyTestCase(TestCase):
def setUp(self):
self.unfinished_task = Task.objects.get(tasked_with="Unfinished Test")
def test_initial_task_deletion_status_is_none(self):
self.assertIsNone(self.unfinished_task.delete_status)
# Make sure a task's deletion status changes appropriately
def test_unfinished_task_deletion_status_updates_appropriately(self):
self.unfinished_task.timed_delete(delta=.1)
self.assertIs(self.unfinished_task.delete_status, "Marked for Deletion")
You can place the repeated line in the setUp method, and that will make your code less repetitive, but as DanielRoseman pointed out, it will still be run for each test, so you won't be using the same query result.
You can place it in the setUpTestData method, and it will be run only once, before all the tests in MyTestCase, but then your unfinished_task object will be a class variable, shared across all the tests. In-memory modifications made to the object during one test will carry over into subsequent tests, and that is not what you want.
In read-only tests, using setUpTestData is a good way to cut out unnecessary queries, but if you're going to be modifying the objects, you'll want to start fresh each time.

Django DRF APITestCase chain test cases

For example I want to write several tests cases like this
class Test(APITestCase):
def setUp(self):
....some payloads
def test_create_user(self):
....create the object using payload from setUp
def test_update_user(self):
....update the object created in above test case
In the example above, the test_update_user failed because let's say cannot find the user object. Therefore, for that test case to work, I have to create the user instead test_update_user again.
One possible solution, I found is to run create user in setUp. However, I would like to know if there is a way to chain test cases to run one after another without deleting the object created from previous test case.
Rest framework tests include helper classes that extend Django's existing test framework and improve support for making API requests.
Therefore all tests for DRF calls are executed with Django's built in test framework.
An important principle of unit-testing is that each test should be independent of all others. If in your case the code in test_create_user must come before test_update_user, then you could combine both into one test:
def test_create_and_update_user(self):
....create and update user
Tests in Django are executed in a parallell manner to minimize the time it takes to run all tests.
As you said above if you want to share code between tests one has to set it up in the setUp method
def setUp(self):
pass

Django TestCase with fixtures causes IntegrityError due to duplicate keys

I'm having trouble moving away from django_nose.FastFixtureTestCase to django.test.TestCase (or even the more conservative django.test.TransactionTestCase). I'm using Django 1.7.11 and I'm testing against Postgres 9.2.
I have a Testcase class that loads three fixtures files. The class contains two tests. If I run each test individually as a single run (manage test test_file:TestClass.test_name), they each work. If I run them together, (manage test test_file:TestClass), I get
IntegrityError: Problem installing fixture '<path>/data.json': Could not load <app>.<Model>(pk=1): duplicate key value violates unique constraint "<app_model_field>_49810fc21046d2e2_uniq"
To me it looks like the db isn't actually getting flushed or rolled back between tests since it only happens when I run the tests in a single run.
I've stepped through the Django code and it looks like they are getting flushed or rolled back -- depending on whether I'm trying TestCase or TransactionTestCase.
(I'm moving away from FastFixtureTestCase because of https://github.com/django-nose/django-nose/issues/220)
What else should I be looking at? This seems like it should be a simple matter and is right within what django.test.TestCase and Django.test.TransactionTestCase are designed for.
Edit:
The test class more or less looks like this:
class MyTest(django.test.TransactionTestCase): # or django.test.TestCase
fixtures = ['data1.json', 'data2.json', 'data3.json']
def test1(self):
return # I simplified it to just this for now.
def test2(self):
return # I simplified it to just this for now.
Update:
I've managed to reproduce this a couple of times with a single test, so I suspect something in the fixture loading code.
One of my basic assumptions was that my db was clean for every TestCase. Tracing into the django core code I found instances where an object (in one case django.contrib.auth.User) already existed.
I temporarily overrode _fixture_setup() to assert the db was clean prior to loading fixtures. The assertion failed.
I was able to narrow the problem down to code that was in a TestCase.setUpClass() instead of TestCase.setUp(), and so the object was leaking out of the test and conflicting with other TestCase fixtures.
What I don't understand completely is I thought that the db was dropped and recreated between TestCases -- but perhaps that is not correct.
Update: Recent version of Django includes setUpTestData() that should be used instead of setUpClass()