Django UnitTest with Mock

12,729

Solution 1

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

Solution 2

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
Share:
12,729
Jerry Meng
Author by

Jerry Meng

Updated on June 04, 2022

Comments

  • Jerry Meng
    Jerry Meng almost 2 years

    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?