Django - Writing unit tests with mocks - python

I started working on a Django project recently. To start off with, I read the book on TDD with Python and the official documentation (for tests). And a few more blogs as well.
One thing I notice is, they write tests that access the database models. Consider the following snippet from here
def test_home_page_can_save_a_POST_request(self):
request = HttpRequest()
request.method = 'POST'
request.POST['item_text'] = 'A new list item'
response = home_page(request)
self.assertEqual(Item.objects.count(), 1)
new_item = Item.objects.first()
self.assertEqual(new_item.text, 'A new list item')
The test asserts whether the number of Item objects is 1. So the test actually adds and retrieves data from the database. Wouldn't that make the test slow?
If the tests are parallelized, this test case may fail if there is another test which adds an Item object, right?
How about patching methods / objects? The above snippet can be refactored like this
#patch('my_app.views.Item')
def test_home_page_can_save_a_POST_request(self, mock_item):
request = HttpRequest()
request.method = POST
request.POST['item_text'] = 'A new list item'
response = home_page(request)
self.assertTrue(mock_item.objects.create.called)
I am new to Django and I am not familiar with the practices. The tutorials I visited wrote tests that talk to database. I would like to know, whether that is the convention for testing in Django projects. Is the second snippet (using patches and mock) perfectly fine in the Django ecosystem?
Edit: Same with Forms as well - Mocking form.is_valid method to return True or False, provided there are separate unit tests for Forms.
P.S. TDD with Python is an awesome book that helped me to a great extend in on-boarding. I definitely recommend that to anyone learning Django.

If you want to test your models, you can hardly bypass the database, and yes indeed it can make the tests somewhat slower, even if using an in-memory SQLite db - which can lead to other problems FWIW since SQLite is not a transparant replacement for something like PostgreSQL.
But assuming whatever model or or other function / object / method you call in your views have their own unittest and you only want to check your views do the expected calls, mocking is viable strategy - actually probably saner than testing the results of the expected calls (which mostly turn your unittests into integration tests).

Related

How to create a Django REST Framework View instance from a ViewSet class?

I'm trying to unit test Django REST Framework view set permissions for two reasons: speed and simplicity. In keeping with these goals I would also like to avoid using any mocking frameworks. Basically I want to do something like this:
request = APIRequestFactory().post(…)
view = MyViewSet.as_view(actions={"post": "create"})
self.assertTrue(MyPermission().has_permission(request, view))
The problem with this approach is that view is not actually a View instance but rather a function which does something with a View instance, and it does not have certain properties which I use in has_permission, such as action. How do I construct the kind of View instance which can be passed to has_permission?
The permission is already tested at both the integration and acceptance level, but I would like to avoid creating several complex and time-consuming tests to simply check that each of the relevant actions are protected.
I've been able to work around this by monkeypatching a view set instance and manually dispatching it:
view_set = MyViewSet()
view_set.action_map = {"post": "create"}
view_set.dispatch(request)
You can do something like below.
request = APIRequestFactory().post(…)
view_obj = MyViewSet()
self.assertTrue(MyPermission().has_permission(request, view_obj))

In a Django test, how should I save a database object and then retrieve it from the database?

I am using Django 1.8. I wrote the following code to test that a pre_save hook works correctly, but this code seems very inelegant. Is this the "proper way" to write this type of unit test?
class PreSaveTests(TestCase):
def test_pre_save_hook(self):
person = Person(name="Joe")
person.save()
person2 = Person.objects.get(pk = person.pk)
# Confirm that the pre_save hook ran.
# The hook sets person.is_cool to True.
self.assertEqual(person2.is_cool, True)
This works fine, but it seems ugly.
The really ugly part is that person and person2 are the same database object. The only difference is that person2 was retrieved from the database.
What you're doing in your test is perfectly fine. You can however simplify / improve it a little in my opinion.
I think you should use factories (you can use FactoryBoy). This way you won't have to update your test when you add/remove mandatory fields on your model. Also, you can remove irrelevant information from your test. In this case, the fact that the person name is Joe is completely irrelevant.
You can replace:
person = Person(name="Joe")
person.save()
with:
person = PersonFactory.create()
As Daniel mentioned, you don't need to reload the Person instance. So you don't have to do this:
person2 = Person.objects.get(pk = person.pk)
Finally, a small tip, you can use assertTrue instead of assertEquals(something, True):
class PreSaveTests(TestCase):
def test_pre_save_hook(self):
person = PersonFactory.create()
self.assertTrue(person.is_cool)
Firstly, I'm not sure why you think that's ugly: seems a perfectly reasonable way to test this functionality.
However, you could definitely make it simpler. Although Django instances don't have identity - that is, two instances retrieved from the database separately won't share modifications until they are saved and retrieved - when the pre-save hook runs, it modifies the existing instance. So in fact person will get the modification to set is_cool, so there is no need to retrieve and check person2.
You could directly check the property in the query, without actually getting an object:
class PreSaveTests(TestCase):
def test_pre_save_hook(self):
person = Person(name="Joe")
person.save()
# Confirm that the pre_save hook ran.
# The hook sets person.is_cool to True.
self.assertTrue(
Person.objects.filter(pk = person.pk, is_cool=True).exists()
)
I think that's a good way to test simple functionality. However, a "by the book" unit test would be better defined by mocking the database functionality.
This way you can unit test your methods without caring about what the database is doing.
I normally do this with the mock library (included in 3.x). Without going into much detail as it's has been described in other answers, you can use a patch to mock the model you're testing (Person) and then make it return something.
Take a look at mock-django as well, it provides lots of functionality related to this, https://pypi.python.org/pypi/mock-django and here https://docs.python.org/3/library/unittest.mock.html
I can't test this (and I'll make it more explicit that I'd normally) for Python 3. Inside a unittest class, you can create a test like this.
# first patch your class
#patch('my_app_name.models.Person')
def test_my_person(self, person_mock)
person_mock.objects = MagicMock()
person_mock.objects.configure_mock(get.return_value='guy_number_1')
# then you can test your method. For example if your method change the guy name.
self.assertEquals(my_method('guy_number_1'), 'GUY_NUMBER_1')
The code is not the best but the idea is that you're mocking the database, so if your database connection brakes, your unittest don't (as it should be because you aren't testing Django functionality nor your database connection).
This has been useful for me when doing automatic building and testing without having to deploy a test database. Then you can add integration tests to cover your database functionality.
I'll extend the explanation if it isn't clear enough.
Useful things sometimes overlooked in mock are the configure method, side_effect for mocking exceptions and sometimes you will need to reload your module to apply the patches.
A bit late on this question but there is a refresh_from_db() function in Django 3.2,
So you can run:
person = Person(name="Geof")
person.save()
person.refresh_from_db()
https://docs.djangoproject.com/en/3.2/ref/models/instances/#refreshing-objects-from-database

Django Test Creating Forms with New IDs

I have just moved all of my AJAX validation code over to Django Forms. I need some help updating my tests. I basically have some test data, declared as constants, that are used across all suites. I am then this data repeatedly throughout my tests.
As part of the setup I create some users and login the user that I need:
def setUp(self):
self.client = Client()
create_user(username='staff', email='staff#staff.com',
password='staff', staff=True)
create_user(username='agent', email='agent#agent.com',
password='agent', staff=False)
ShiftType.objects.create(type_id='SI', description='Sales Inbox')
self.client.login(username='staff', password='staff')
The tear down deletes this data (or it used to):
def tearDown(self):
# Clean up the DB
self.client.logout()
ShiftType.objects.all().delete()
User.objects.all().delete()
Event.objects.all().delete()
RecurrentEvent.objects.all().delete()
This was working fine, but now the form does not validate because the form value id given by the users is incremented each time. For example:
ERROR: <ul class="errorlist"><li>employee_id<ul class="errorlist"><li>Select a valid choice. That choice is not one of the available choices.</li></ul></li></ul>
Printing the form allows me to see that the ids are being incremented each time.
Why is this happening even though I am deleting all employees?
I would look into using text fixtures rather than creating and deleting the data every time. This is pretty easy to do in Django. In your tests.py it would look something like this
class BlogTest(TestCase):
fixtures = ['core/fixtures/test.json']
When you do this django will build you a test database, load the fixture into it, run your tests, and then blow away the database after the tests are done. If you want to even use a different database engine (we do this to have our tests use sqlite because it is fast) you can throw something like this into your settings.py
This will make it so the ID's are the same every single time which would fix your problem.

What kind of tests should one write in Django

Let's says I have a Djano app. Users can sign up, get a activation mail, activate their accounts and log in. After logging in, users can can create, update and delete objects rhough a custom Form which uses the Manager to handle the Model.
What should I be testing here — should I use the request framework to make requests and test the whole chain via the Views and Forms or should I be writing unit tests to test the Manager and the Model?
When testing the whole chain, I get to see that the URLs are configured properly, the Views work as expecvted, the Form cleans the data properly and it would also test the Models and Managers. It seems that the Django test framework is more geared toward unit-testing than this kind of test. (Is this something that should be tested with Twill and Selenium?)
When writing unit tests, I would get to test the Manger and the Models but the URLs and the Forms don't really come into play, do they?!
A really basic question but I'd like to get some of the fundamentals correct.
Thank you everyone.
Yes, Django unit tests, using the Client feature, are capable of testing whether or not your routes and forms are correct.
If you want full-blown behavior-driven testing from the outside, you can used a BDD framework like Zombie.
As for which tests you need, Django author Jacob Kaplan-Moss answered the question succinctly: "All of them."
My general testing philosophy is to work until something stupid happens, then write a test to make sure that stupid thing never happens again.

Simple RESTFUL client/server example in Python?

Is there an online resource that shows how to write a simple (but robust) RESTFUL server/client (preferably with authentication), written in Python?
The objective is to be able to write my own lightweight RESTFUL services without being encumbered by an entire web framework. Having said that, if there is a way to do this (i.e. write RESFUL services) in a lightweight manner using Django, I'd be equally interested.
Actually, coming to thing of it, I may even PREFER a Django based solution (provided its lightweight enough - i.e. does not bring the whole framework into play), since I will be able to take advantage of only the components I need, in order to implement better security/access to the services.
Well, first of all you can use django-piston, as #Tudorizer already mentioned.
But then again, as I see it (and I might be wrong!), REST is more of a set of design guidelines, rather than a concrete API. What it essentially says is that the interaction with your service should not be based on 'things you can do' (typical RPC-style methods), but rather 'things, you can act on in predictable ways, organized in a certain way' (the 'resource' entity and http verbs).
That being said, you don't need anything extra to write REST-style services using django.
Consider the following:
# urlconf
from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('',
url(r'^tickets$', 'myapp.views.tickets', name='tickets'),
url(r'^ticket/(?P<id>\d+)$', 'myapp.views.tickets', name='ticket'),
url(r'^ticket$', 'myapp.views.tickets', name='ticket'),
)
# views
def tickets(request):
tickets = Ticket.objects.all()
return render_to_response('tickets.html', {'tickets':tickets})
def ticket(request, id=None):
if id is not None:
ticket = get_object_or_404(Ticket, id=id)
if request.method == 'POST':
# create or update ticket here
else:
# just render the ticket (GET)
...
... and so on.
What matters is how your service is exposed to its user, not the library/toolkit/framework it uses.
This one looks promising. http://parand.com/say/index.php/2009/04/30/django-piston-rest-framework-for-django/ I've used it before and it's pretty nifty. Having said that, it doesn't seem maintained recently.

Categories