【问题标题】:Patching multiple ORM calls in Django在 Django 中修补多个 ORM 调用
【发布时间】:2017-07-04 05:58:45
【问题描述】:

我的 Django 应用程序中有一个服务层,其中有一些业务逻辑和相应的数据检索代码。现在,当我编写单元测试时,我想模拟对 ORM 的调用,以便我只测试服务层

我的服务层如下所示:

from .models import Event

class EventService():

    def get_client_separated_events(self):
        # Some complex business logic goes here
        qs = Event.objects.all()
        # Some complex business logic here as well
        return foo

当我测试EventService 类时,我希望完全模拟出诸如Events.objects.all() 之类的调用。截至目前,我正在使用patch 来实现这一点:

class TestEventService(TestCase):

@classmethod
def setUpTestData(cls):
    cls.event_service = EventService()
    cls.dummy_events = [Event(id=1, name='n1', description='d1'), Event(id=2, name='n2', description='d2')]

@patch('foo.bar.Events.objects.all')
def test_get_all(self, get_events):
    get_events.return_value = self.dummy_events
    expected_events = self.event_service.get_all()
    self.assertListEqual(expected_Events, self.dummy_events, msg="Assertion failed for get_all method "
                                                                     "in Event Service")

但是,可能有十个这样不同的 ORM 调用,我不想写十行 @patch 语句来模拟它们。我在网上看到@patch.multiple 可以做到这一点——但我不太清楚这是否是正确的做法。有人可以帮忙吗?

【问题讨论】:

    标签: python django unit-testing orm


    【解决方案1】:

    虽然您可以直接模拟 ORM 并返回任意结果,但我认为使用 factory_boy 之类的库在数据库中插入假数据并保持 ORM 调用不被模拟会更有效:

    class EventFactory(factory.django.DjangoModelFactory):
        class Meta:
            model = 'myapp.Event'
    
    
    class TestEventService(TestCase):
    
        @classmethod
        def setUpTestData(cls):
            cls.event_service = EventService()
    
        def test_get_all(self):
            expected = [EventFactory() for i in range(10)]
            self.assertListEqual(
                self.event_service.get_all(),
                expected)
    

    在某些时候,你不能模拟所有东西,如果你模拟 ORM 调用,你最终可能会通过测试,而你的代码实际上是错误的(例如,如果你在 filterexclude 语句中有错误.

    如果您可以轻松测试它,并保持测试快速高效,我认为您应该避免嘲笑某些东西。在你的情况下:

    • 您可以轻松创建虚拟数据
    • 如果您使用 ORM,您的测试仍然会很快,因为 Django 在测试期间默认使用内存中的 SQLite 数据库,避免了磁盘 I/O。

    不过,模拟在其他情况下完全有意义,例如使用 HTTP API 通过网络检索数据。

    【讨论】:

      【解决方案2】:

      在我们的项目中,我们试图决定什么时候模拟 ORM 层是个好主意,什么时候不适合。模拟 ORM 层使测试运行得更快,但测试更脆弱。

      对我来说,如果查询中有任何复杂性,似乎不值得嘲笑 ORM。您应该针对包含一些示例数据的测试数据库测试复杂查询。

      如果您确实有想要模拟的简单查询,请尝试使用 django-mock-queries 包。我贡献了一个名为 mocked_relations 的装饰器,它模拟了您所询问的内容:Event.objects 和外键关系。

      看看example,它展示了如何使用它。您可以列出几个要模拟的模型类。

      @mocked_relations(User)
      class TestMockedApi(TestCase):
          def setUp(self):
              self.api = AnalyticsApi()
      
          def test_api_active_users_filters_by_is_active_true(self):
              active_user = MockModel(mock_name='active user', is_active=True)
              inactive_user = MockModel(mock_name='inactive user', is_active=False)
      
              User.objects.add(*[active_user, inactive_user])
              results = [x for x in self.api.active_users()]
      
              assert active_user in results
              assert inactive_user not in results
      

      【讨论】:

        猜你喜欢
        • 2015-07-09
        • 2021-09-17
        • 1970-01-01
        • 1970-01-01
        • 2021-11-03
        • 2021-03-04
        • 2019-09-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多