带模拟的 Django 单元测试

Django UnitTest with Mock

我正在为基于 Django class 的视图编写单元测试。

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

关键问题是 call_external_API()get_data().

当我写单元测试的时候,我真的不想调用外部API来获取数据。首先,那会花我的钱;其次,我可以轻松地在另一个测试文件中测试 API。

我也可以通过仅对其进行单元测试并模拟 call_external_API().

的输出来轻松测试此 get_data() 方法

但是,当我测试整个基于 class 的视图时,我会做

self.client.get('/example/url/')

并检查状态代码和上下文数据以进行验证。

在这种情况下,当我测试整个基于 class 的视图时,如何模拟此 call_external_API()

在测试基于分类的视图时,您可以模拟 call_external_api() 方法:

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

您要找的是patch from unittest.mock。您可以通过 MagicMock() 对象修补 call_external_api()

也许您想为 class 中的所有测试修补 call_external_api()patch 给你两种基本的方法

  • 装饰测试class
  • 分别在setUp()tearDown()中使用start()stop()

patch装饰器装饰一个class就像装饰所有的测试方法一样(详见文档)实现起来会很整洁。按照示例假设您的视图在 my_view 模块中。

@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)

可以构建更复杂的示例,您可以检查如何调用 mock_call_external_api 并为您的 API 设置 return 值或副作用。

我没有给出任何关于启动和停止方式的例子(我不太喜欢)但我想花一些时间在两个 细节上 :

  1. 我假设在你的 my_view 模块中你定义了 call_external_api 或者你通过 from my_API_module import call_external_api 导入它否则你应该注意 Where to patch
  2. 我用过 autospec=True:恕我直言,它应该在每个补丁调用中使用,documentation 很好地解释了为什么