Load Django Fixtures Once? - python

I know this question was asked before, but I was wanting to see if there is a more updated solution. Would there be a way to load all my fixtures in setUp and and flush them when all tests are finished?
Right now, I'm loading in my fixtures like so...
from django.test import TestCase
from django.core.management import call_command
class GlobalSetup(TestCase):
def setUp(self):
# Load fixtures
call_command('loaddata', 'test_cfst.json', verbosity=0)
call_command('loaddata', 'test_lmt.json', verbosity=0)
call_command('loaddata', 'test_qt.json', verbosity=0)
class BaseTest(GlobalSetup):
fixtures = [
'test_cfst.json',
'test_lmt.json',
'test_qt.json'
]
def setUp(self):
super(BaseTest, self).setUp()
def test_base(self):
# Some random tests
With the newer version of django is there a way, or better way, to do this?

I am not sure that you are aware or not but you just load fixtures as below:
from django.test import TestCase
from myapp.models import Animal
class AnimalTestCase(TestCase):
fixtures = ['mammals.json', 'birds']
def setUp(self):
# Test definitions as before.
call_setup_methods()
def test_fluffy_animals(self):
# A test that uses the fixtures.
call_some_test_code()
example from the docs
So you don't need to write GlobalSetup with call_command as you did in your current example which is leading to load the fixtures twice. because the method is already called in setUpClass(refer this link)

Related

pytest monkeypatch.setattr() inside of test class method

I have a test class with few test methods and I want to patch some app classes and methods from the test methods.
In pytest docs I found an example of how to use monkeypatch module for tests. It that example all tests are just functions, not testclass methods.
But I have a class with test methods:
class MyTest(TestCase):
def setUp():
pass
def test_classmethod(self, monkeypatch):
# here I want to use monkeypatch.setattr()
pass
And just passing monkeypatch as method param is obviously doesn't work. So looks like py.test magic doesn't work this way.
So the question is simple and maybe stupid: how can I use monkeypatch.setattr() for pytest inside from the test class method?
It can't work in this form
While pytest supports receiving fixtures via test function arguments
for non-unittest test methods, unittest.TestCase methods cannot
directly receive fixture function arguments as implementing that is
likely to inflict on the ability to run general unittest.TestCase test
suites.
You might create monkeypatch directly
from _pytest.monkeypatch import MonkeyPatch
class MyTest(TestCase):
def setUp():
self.monkeypatch = MonkeyPatch()
def test_classmethod(self):
self.monkeypatch.setattr ...
...
or create own fixture, which will add monkeypatch to your class, and use #pytest.mark.usefixtures
#pytest.fixture(scope="class")
def monkeypatch_for_class(request):
request.cls.monkeypatch = MonkeyPatch()
#pytest.mark.usefixtures("monkeypatch_for_class")
class MyTest(TestCase):
def setUp():
pass
def test_classmethod(self):
self.monkeypatch.setattr ...
...
I had exactly the same problem.
This works perfectly
import unittest
import pandas as pd
from _pytest.monkeypatch import MonkeyPatch
from src.geipan_data import loadLongitudeLatitudeDateTestimony
class TestGeipanData(unittest.TestCase):
def setUp(self):
self.monkeypatch = MonkeyPatch()
def test_loadLongitudeLatitudeDateTestimony(self):
def read_csv(*args, **kwargs):
return pd.DataFrame({
'obs_date_heure': ['2010-05-21', '1926-05-21'],
'obs_1_lon': [45.123, 78.4564],
'obs_1_lat': [32.123, 98.4564],
})
self.monkeypatch.setattr(pd, 'read_csv', read_csv)
df = loadLongitudeLatitudeDateTestimony()
self.assertListEqual(
df.columns.values.tolist(),
['obs_date_heure', 'obs_1_lon', 'obs_1_lat']
)
In this example I do mock the pd.read_csv method with monkey patch and I uses asserListEqual that extends from unittest.TestCase

Many Testcase classes inside a test.py in django don't run correctly

I want to have different Testcase classes inside my test.py in my django project. One for Model testing (crud) one for view testing etc. I have the following classes
class EntryModelTest(TestCase):
LOG = logging.getLogger(__name__)
#classmethod
def setup_database(cls):
#database initialization
#classmethod
def setUpClass(cls):
super(EntryModelTest, cls).setUpClass()
cls.setup_database()
cls.myModel = MyModel.objects.get(pk=1)
#some queries (additions) that use things that were added in setup_database()
and a second testcase
class MyCalViewsTest(TestCase):
LOG = logging.getLogger(__name__)
#classmethod
def setup_database(cls):
#exactly the same code as above testcase(I know I could use Inheretiance. Will refactor)
#classmethod
def setUpClass(cls):
#The same as above testcase
cls.myModel = MyModel.objects.get(pk=1) #this gives the following error: DoesNotExist: PracticeData matching query does not exist.
The MyModel instance is created in setup_database method. I am not quite sure why. If the test database is destroyed after every testcase class is executed then the setup_database method should have recreated the MyModel instance with pk=1 so I would have existed. If it doesn't destroy the test database then the MyModel instance with pk=1 already exists so I shouldn't have any problems also. Any help would be appreciated.

Django UnitTest with Mock

I am writing an Unit-Test for a Django class-based view.
class ExampleView(ListView):
def get_context_data(self, **kwargs):
context = super(EampleView, self).get_context_data(**kwargs)
## do something else
def get_queryset(self, **kwargs):
return self.get_data()
def get_data(self):
call_external_API()
## do something else
The key issue is that call_external_API() in get_data().
When I am writing Unit-test, I don't really want to call external API to get data. First, that will cost my money; second, I can easily test that API in another test file.
I also can easily test this get_data() method by having an unit-test only for it and mock the output of call_external_API().
However, when I test this whole class-based view, I simply will do
self.client.get('/example/url/')
and check the status code and context data to verify it.
In this case, how do I mock this call_external_API() when I am testing the whole class-based view?
What your are looking for is patch from unittest.mock. You can patch call_external_api() by a MagicMock() object.
Maybe you want to patch call_external_api() for all your tests in class. patch give to you essentialy two way to do it
decorate the test class
use start() and stop() in setUp() and tearDown() respectively
Decorate a class by patch decorator is like decorate all test methods (see documentation for details) and the implementation will be very neat. Follow example assume that your view is in my_view module.
#patch("my_view.call_external_api", autospec=True)
class MyTest(unittest.TestCase):
def setUp(self):
self.client = Client()
def test_get_data(self, mock_call_external_api):
self.client.get('/example/url/')
self.assertTrue(mock_call_external_api.called)
More sophisticate examples can be build and you can check how you call mock_call_external_api and set return value or side effects for your API.
I don't give any example about start and stop way to do it (I don't really like it) but I would like to spend some time on two details:
I assumed that in your my_view module you define call_external_api or you import it by from my_API_module import call_external_api otherwise you should pay some attention on Where to patch
I used autospec=True: IMHO it should be used in every patch call and documentation explain why very well
You can mock the call_external_api() method when testing the classed based view with something like this:
import modulea
import unittest
from mock import Mock
class ExampleTestCase(unittest.TestCase):
def setUp(self):
self.call_external_api = modulea.call_external_api
def tearDown(self):
modulea.call_external_api = self.call_external_api
def get_data(self):
modulea.call_external_api = Mock(return_value="foobar")
modulea.call_external_api()
## do something else

Patching(mocking) forms form in django tests

I tried to mock form with mock.patch and can`t. I have this code
forms.py
class CreatePostForm(object):
pass
views.py:
from forms import CreatePostForm
def doit():
print CreatePostForm()
and I want to to test this view in isolation. I tried to patch form with mock.patch and i wrote something like that:
tests.py:
from mock import patch
import views
with patch('forms.CreatePostForm') as Form:
views.doit()
I tried to google for solution and find nothing
Answered: thanks #dstanek for good answer and good sample of code
When you use patch you specify the target of the object you want to mock. This is usually the place where it is imported, not where it is defined.
This is because by the time your test runs the views module has already been imported. If you are importing the class like I'm doing in my example below then the views module will contain a reference to the forms.CreatePostForm. So changing forms.CreatePostForm would not change this reference. Things would be different if you imported the module as specified forms.CreatePostForm in your view.
I've included an extremely minimal example below.
forms.py
class CreatePostForm(object):
pass
views.py:
from forms import CreatePostForm
def doit():
print CreatePostForm()
tests.py:
from mock import patch
import views
with patch('views.CreatePostForm') as Form:
views.doit()

How to setup and teardown temporary django db for unit testing?

I would like to have a python module containing some unit tests that I can pass to hg bisect --command.
The unit tests are testing some functionality of a django app, but I don't think I can use hg bisect --command manage.py test mytestapp because mytestapp would have to be enabled in settings.py, and the edits to settings.py would be clobbered when hg bisect updates the working directory.
Therefore, I would like to know if something like the following is the best way to go:
import functools, os, sys, unittest
sys.path.append(path_to_myproject)
os.environ['DJANGO_SETTINGS_MODULE'] = 'myapp.settings'
def with_test_db(func):
"""Decorator to setup and teardown test db."""
#functools.wraps
def wrapper(*args, **kwargs):
try:
# Set up temporary django db
func(*args, **kwargs)
finally:
# Tear down temporary django db
class TestCase(unittest.TestCase):
#with_test_db
def test(self):
# Do some tests using the temporary django db
self.fail('Mark this revision as bad.')
if '__main__' == __name__:
unittest.main()
I should be most grateful if you could advise either:
If there is a simpler way, perhaps subclassing django.test.TestCase but not editing settings.py or, if not;
What the lines above that say "Set up temporary django db" and "Tear down temporary django db" should be?
Cracked it. I now have one python file completely independent of any django app that can run unit tests with a test database:
#!/usr/bin/env python
"""Run a unit test and return result.
This can be used with `hg bisect`.
It is assumed that this file resides in the same dir as settings.py
"""
import os
from os.path import abspath, dirname
import sys
import unittest
# Set up django
project_dir = abspath(dirname(dirname(__file__)))
sys.path.insert(0, project_dir)
os.environ['DJANGO_SETTINGS_MODULE'] = 'myproject.settings'
from django.db import connection
from django.test import TestCase
from django.test.utils import setup_test_environment, teardown_test_environment
from myproject import settings
from myproject.myapp.models import MyModel
class MyTestCase(TestCase):
def test_something(self):
# A failed assertion will make unittest.main() return non-zero
# which if used with `hg bisect` will mark the revision as bad
self.assertEqual(0, len(MyModel.objects.all())) # and so on
if '__main__' == __name__:
try:
setup_test_environment()
settings.DEBUG = False
verbosity = 0
old_database_name = settings.DATABASE_NAME
connection.creation.create_test_db(verbosity)
unittest.main()
finally:
connection.creation.destroy_test_db(old_database_name, verbosity)
teardown_test_environment()
You must use the internal Django TestCase to do so.
from django.test import TestCase
class TestCase(TestCase):
# before every call to setUp(), the db is automatically
# set back to the state is was after the first syncdb then
# all these fixture files will be loaded in the db
fixtures = ['mammals.json', 'birds']
# put whatever you want here, you don't need to call the
# super()
def setUp(self):
# Test definitions as before.
call_setup_methods()
def test(self):
# Do some tests using the temporary django db
self.fail('Mark this revision as bad.')
It's fully compatible with unittest so your code don't need to change much.
You can learn more about django.test, fixtures, flush and loaddata commands.
If you do want to use a decorator to do the job, you can use the call_command to use in your python program any django command. e.g :
from django.core.management import call_command
call_command('flush', 'myapp')
call_command('loaddata', 'myapp')

Categories