我的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
可以做到这一点 - 但我不太清楚这是否是正确的方法。有人可以帮忙吗?
答案 0 :(得分: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调用,则最终可能会在代码实际出错的情况下通过测试(例如,如果您的filter
或{{ 1}}陈述。
如果您可以轻松测试,并保持测试快速有效,我认为您应该避免嘲笑某些事情。在你的情况下:
在其他情况下,模拟完全有意义,例如使用HTTP API通过网络检索数据。
答案 1 :(得分:1)
在我们的项目中,我们试图决定何时嘲笑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