3

Is there any way to force the except block to execute so that I can verify the error messages/handling with unit testing? For example:

views.py

def get_books(request):
    ...
    try:
        book = Books.objects.get_or_create(user=request.user)
    except:
        messages.error(request, 'Unable to create new book!')
        return HttpResponseRedirect(reverse('my_books'))

    # more try/except cases

So in this example, I could try to simulate an ORM failure or I can try to send in an invalid user for this case. But is there a general way to throw exceptions (I'm open to using libraries)? Mock for instance. Or is there some other method to use unittest to force exceptions? Obviously there are going to be many try/except blocks throughout the code, so I'm looking for any help.

4
  • Is it enforced with a ForeignKey for Books to have a unique user value? Commented Jan 7, 2015 at 5:40
  • @Duncan not a dupe - this is about covering the view with tests. Completely unrelated to what the other question is about. Thanks. Commented Jan 7, 2015 at 5:41
  • Yeah, I tried searching related. @alecxe, exactly. The problem is that there are going to be hundreds of try/except blocks and I can't inject raises to the view code every time I want to test. Well, maybe that could be done, but it won't be elegant. PS this question is there a general solution for unit testing; not only this specific example I provided. Commented Jan 7, 2015 at 5:43
  • It is clear that you need to use mocking, but, generally speaking, mocking Django's ORM is not trivial as far as I recall. Commented Jan 7, 2015 at 6:54

1 Answer 1

6

You can use unittest.mock to do it. You should patch Books.objects object by patch.object and use side_effect to raise exception. A complete test of your method exception behavior should be:

import unittest
from unittest.mock import patch, ANY
from django.test.client import RequestFactory

class TestDjango(unittest.TestCase):    
    @patch("your_module.messages")
    @patch("your_module.HttpResponseRedirect")
    def test_get_books_exception(self,mock_redirect,mock_messages)
        r = RequestFactory().get('/I_dont_konw_how_to_build_your_request/')
        objs = Books.objects
        with patch.object(objs,"get_or_create", side_effect=Exception()):
              self.assertIs(get_books(r),mock_redirect.return_value)
              mock_messages.error.assert_called_with(r, ANY)
              mock_redirect.assert_called_with(reverse('my_books'))

I used ANY do remove the string dependency. I'm not sure what you want test in your code (complete behavior or just redirect...), anyway I wrote a complete example.


If your project is not a legacy work consider to rewrite your method (and the new django's ORM calls) in TDD style where you have for instance a _get_books_proxy() function that just call the django ORM methods:

def _get_books_proxy(request):
    return Books.objects.get_or_create(user=request.user)

and now patch _get_books_proxy() directly by patch decorator as:

@patch("your_module.messages")
@patch("your_module.HttpResponseRedirect")
@patch("your_module._get_books_proxy", side_effect=Exception())
def test_get_books_exception(self,mock_get_book, mock_redirect, mock_messages)
    r = RequestFactory().get('/I_dont_konw_how_to_build_your_request/')
    self.assertIs(get_books(r),mock_redirect.return_value)
    mock_messages.error.assert_called_with(r, ANY)
    mock_redirect.assert_called_with(reverse('my_books'))

Moreover _get_books_proxy() behavior can be tested by patching Books and passing a simple Mock():

@patch("your_module.Books")
def test__get_books_proxy(self,mock_books):
     r = Mock()
     self.assertIs(mock_books.objects.get_or_create.return_value, your_module._get_books_proxy(r))
     mock_books.objects.get_or_create.assert_called_with(user=r.user)

Now you have a sentinel that can yield to you if your critical point behavior will be changed.

I apologize if you find some errors (I cannot test it)... but I hope the idea is clear.

Sign up to request clarification or add additional context in comments.

1 Comment

I was not able to get the ORM patch to work (not to say your method is wrong, I probably just need to play with it more), but the second proxy function method worked. If I understand the concept, I should write helper functions on difficult-to-test parts of the code, and then unit test using a patch? It works, but doesn't that open the code up to more entry points for failure/security issues? In any event thank you very much for your help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.