pytest-django database initialization doesn't appear to reload the database - python

We have distilled a situation down to the following:
import pytest
from django.core.management import call_command
from foo import bar
#pytest.fixture(scope='session')
def django_db_setup(django_db_setup, django_db_blocker):
LOGGER.info('ran call_command')
with django_db_blocker.unblock():
call_command('loaddata', 'XXX.json')
#pytest.mark.django_db(transaction=True)
def test_t1():
assert len(bar.objects.all())
#pytest.mark.django_db(transaction=True)
def test_t2():
assert len(bar.objects.all())
The test fixture XXX.json includes one bar. The first test (test_t1) succeeds. The second test (test_t2) fails. It appears that the transaction=True attribute does not result in the database being reinitialized with the data from the test fixture.
If TransactionTestCase from unittest is used instead, the initialization happens before every test case in the class and all tests succeed.
from django.test import TransactionTestCase
from foo import bar
class TestOne(TransactionTestCase):
fixtures = ['XXX.json']
def test_tc1(self):
assert len(bar.objects.all())
def test_tc2(self):
assert len(bar.objects.all())
objs = bar.objects.all()
for bar in objs:
bar.delete()
def test_tc3(self):
assert len(bar.objects.all())
I would appreciate any perspectives on why the pytest example doesn't result in a reinitialized database for the second test case.

The django_db_setup is session scoped, and therefore only run once at the beginning of the test session. When using transaction=True, the database gets flushed after every test (including the first) and so any data added in django_db_setup is removed.
TransactionTestCase obviously knows that it is using transactions and because it is a django thing it knows that it needs to re-add the fixtures for each test, but pytest in general is not aware of django's needs, and so it has no way to know that it needs to re-run your fixture django_db_setup – as far as it's concerned it only needs to run that once since it is session scoped.
You have the following options:
use a lower scoped fixture, probably to the function scope as suggested in the comments. But this will probably be opt-in, and this will be run within the transaction, so will be removed after the test is complete.
Write a fixture that is smart / django-aware, and knows when it needs to re-populate that data by detecting when the test is using transactions. But you need to ensure that the database connection being used is not in a transaction. I have done this on django 1.11 and it works fine, although it may need fixing after an upgrade. Looks something like this:
from unittest.mock import patch
from django.core.management import call_command
from django.db import DEFAULT_DB_ALIAS, ConnectionHandler
import pytest
_need_data_load = True
#pytest.fixture(autouse=True)
def auto_loaddata(django_db_blocker, request):
global _need_data_load
if _need_data_load:
# Use a separate DB connection to ensure we're not in a transaction.
con_h = ConnectionHandler()
try:
def_con = con_h[DEFAULT_DB_ALIAS]
# we still need to unblock the database because that's a test level
# constraint which simply monkey patches the database access methods
# in django to prevent access.
#
# Also note here we need to use the correct connection object
# rather than any default, and so I'm assuming the command
# imports `from django.db import connection` so I can swap it.
with django_db_blocker.unblock(), patch(
'path.to.your.command.modules.connection', def_con
):
call_command('loaddata')
finally:
con_h.close_all()
_need_auto_sql = False
using_transactional_db = (
'transactional_db' in request.fixturenames
or 'live_server' in request.fixturenames
)
if using_transactional_db:
# if we're using a transactional db then we will dump the whole thing
# on teardown, so need to flag that we should set it up again after.
_need_data_load = True

Related

Why is test case failing in Django when it returns True in Python intrepreter?

When I run this code in the Python interpreter, it returns True:
>>> from movies.models import Movie
>>> movie_list = Movie.objects.all()
>>> bool(movie_list)
True
When I run my test case, python3 manage.py test movies, it fails:
from django.test import TestCase
from .models import Movie
class QuestionMethodTests(TestCase):
def test_movie_list_empty(self):
movie_list = Movie.objects.all()
self.assertEqual(bool(movie_list), True)
What am I missing? Shouldn't the test pass?
I see. Does that mean the test cases only test the code but can't use
any of the actual database content in its tests?
By default no, and you don't want to mess with the actual DB anyway,
there is a usual way to provide the initial objects for the tests (the actual source can differ, e.g. loading from a file)
from django.test import TestCase
from .models import Movie
class QuestionMethodTests(TestCase):
def setUp(self):
# You can create your movie objects here
Movie.objects.create(title='Forest Gump', ...)
def test_movie_list_empty(self):
movie_list = Movie.objects.all()
self.assertEqual(bool(movie_list), True)
The TestCase class also contains a setUpTestData method if you fancy that, https://docs.djangoproject.com/en/1.8/topics/testing/tools/#django.test.TestCase.setUpTestData
PS: test_movie_list_empty name sounds weird, cause it seems to test that the movie list is NOT empty
Because in tests you are using a temporary database which doesn't have the objects:
Tests that require a database (namely, model tests) will not use your
“real” (production) database. Separate, blank databases are created
for the tests.
Regardless of whether the tests pass or fail, the test databases are
destroyed when all the tests have been executed.
It's dangerous to use the real database for tests. Especially that tests should be reproducible, on other machines too. You should use fixtures for tests. Look at factory_boy.

Google App Engine python datastore stub storing references?

I am trying to do a simple test of a model. I insert and retrieve the model and check all that data I inserted with is present. I expect this test to fail with a simple, blank model, but it passes. Is this a quirk of the testing framework that I have to live with? Can I set an option to prevent it from keeping refs to python objects?
In the following, I expect it to fail at line 30, but it does not. It fails at the ref comparison as I insists the refs be different and they are not..
import unittest
from google.appengine.ext import ndb
from google.appengine.ext import testbed
class Action(ndb.Model): pass
class ActionTestCase(unittest.TestCase):
def setUp(self):
# First, create an instance of the Testbed class.
self.testbed = testbed.Testbed()
# Then activate the testbed, which prepares the service stubs for use.
self.testbed.activate()
self.testbed.init_datastore_v3_stub()
self.testbed.init_memcache_stub()
def tearDown(self):
self.testbed.deactivate()
def testFetchRedirectAttribute(self):
act = Action()
act.attr = 'test phrase'
act.put()
self.assertEquals(1, len(Action.query().fetch(2)))
fetched = Action.query().fetch(2)[0]
self.assertEquals(fetched.attr, act.attr)
self.assertTrue(act != fetched)
if __name__ == '__main__':
unittest.main()
Models are defined as being equal if all of their properties are equal. If you care about identity instead (you probably shouldn't...), then you can use assertIs in your test.
As it turns out, storing refs is the behavior of stubs. However, for TDD purposes, we do need to check if a property is defined in the model. The simple way to do so is to use keyword argument. If I write the test as follows, then it fails as expected.
def testFetchRedirectAttribute(self):
act = Action(attr='test phrase)
act.put()
The solved my immediate problem of having a failing that that I could code against.

Mocking django's database save

I'm trying mock out a save method call on a django models.Model.
I'm using Mock as my mocking library.
I'm testing a function in the file house_factory.py , which is located at apps.deps.house_factory.
house_factory.py:
from apps.market.models import House
def create_house(location, date, price):
house = House(id=None, date, price)
house.save()
# calculate some stuff and further expand the house instance
# for example house.tag.add("some-tag")
# save after calculations
house.save()
I'd like to mock out the House model.
class HouseModelMock(mock.Mock):
def save(self):
pass
Testing method, is part of a unittest.TestCase class
#patch('apps.deps.house_factory.House', new_callable=HouseModelMock)
def create_house_test(self, MockedHouse):
""" Constants """
DAYS_FROM_TODAY = 55
DATE = datetime.date.today() + datetime.timedelta(days=DAYS_FROM_TODAY)
PRICE = 250000
# A location is also a django module , I'm using factory_boy here for building a 'mocked' location
location = LocationFactory.build()
create_house(DATE, PRICE)
MockedHouse.assert_called_with(None, DATE, PRICE)
MockedHouse.save.assert_called_with()
If I run this test I'm getting a:
call__
return self.call(*arg, **kw)
MemoryError
This is one of my first attempts to get serious with django and testing. So maybe I'm setting things up wrong, to mock out a database call.
Any help is appreciated,
Jonas.
"This is one of my first attempts to get serious with django and testing" ... you don't need to mock database saves as Django automatically creates a test DB to run your test suite against whenever you run python manage.py test. Then simply assert the values stored in your DB.
Ideally mock is used to patch own tests (and logic), rather than the default Django ones.
Tip: use an in memory db for unit tests, such as sqlite put the below in your settings.py file:
if 'test' in sys.argv:
DATABASES['default']['ENGINE'] = 'sqlite3'
This will significantly speed up your test run.

Problems using User model in django unit tests

I have the following django test case that is giving me errors:
class MyTesting(unittest.TestCase):
def setUp(self):
self.u1 = User.objects.create(username='user1')
self.up1 = UserProfile.objects.create(user=self.u1)
def testA(self):
...
def testB(self):
...
When I run my tests, testA will pass sucessfully but before testB starts, I get the following error:
IntegrityError: column username is not unique
It's clear that it is trying to create self.u1 before each test case and finding that it already exists in the Database. How do I get it to properly clean up after each test case so that subsequent cases run correctly?
setUp and tearDown methods on Unittests are called before and after each test case. Define tearDown method which deletes the created user.
class MyTesting(unittest.TestCase):
def setUp(self):
self.u1 = User.objects.create(username='user1')
self.up1 = UserProfile.objects.create(user=self.u1)
def testA(self):
...
def tearDown(self):
self.up1.delete()
self.u1.delete()
I would also advise to create user profiles using post_save signal unless you really want to create user profile manually for each user.
Follow-up on delete comment:
From Django docs:
When Django deletes an object, it
emulates the behavior of the SQL
constraint ON DELETE CASCADE -- in
other words, any objects which had
foreign keys pointing at the object to
be deleted will be deleted along with
it.
In your case, user profile is pointing to user so you should delete the user first to delete the profile at the same time.
If you want django to automatically flush the test database after each test is run then you should extend django.test.TestCase, NOT django.utils.unittest.TestCase (as you are doing currently).
It's good practice to dump the database after each test so you can be extra-sure you're tests are consistent, but note that your tests will run slower with this additional overhead.
See the WARNING section in the "Writing Tests" Django Docs.
Precisely, setUp exists for the very purpose of running once before each test case.
The converse method, the one that runs once after each test case, is named tearDown: that's where you delete self.u1 etc (presumably by just calling self.u1.delete(), unless you have supplementary specialized clean-up requirements in addition to just deleting the object).

I need a sample of python unit testing sqlalchemy model with nose

Can someone show me how to write unit tests for sqlalchemy model I created using nose.
I just need one simple example.
Thanks.
You can simply create an in-memory SQLite database and bind your session to that.
Example:
from db import session # probably a contextbound sessionmaker
from db import model
from sqlalchemy import create_engine
def setup():
engine = create_engine('sqlite:///:memory:')
session.configure(bind=engine)
# You probably need to create some tables and
# load some test data, do so here.
# To create tables, you typically do:
model.metadata.create_all(engine)
def teardown():
session.remove()
def test_something():
instances = session.query(model.SomeObj).all()
eq_(0, len(instances))
session.add(model.SomeObj())
session.flush()
# ...
Check out the fixture project. We used nose to test that and it's also a way to declaratively define data to test against, there will be some extensive examples for you to use there!
See also fixture documentation.

Categories

Resources