Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 937
Jerry Meng Avatar asked Mar 02 '15 18:03

Jerry Meng


2 Answers

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:

  1. 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
  2. I used autospec=True: IMHO it should be used in every patch call and documentation explain why very well
like image 122
Michele d'Amico Avatar answered Sep 18 '22 17:09

Michele d'Amico


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
like image 28
Alexander Avatar answered Sep 18 '22 17:09

Alexander