2017-02-15 2 views
1

У меня есть сервисный уровень в моем приложении Django, где у меня есть бизнес-логика и соответствующий код поиска данных. Теперь, когда я пишу модульные тесты, я хочу, чтобы выкрикивал вызовы ORM, так что я тестирую только уровень сервиса.Исправление нескольких вызовов ORM в Django

Моего уровень услуг выглядит следующим образом:

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 может это сделать, но я не совсем знаю, правильно ли это сделать. Может кто-нибудь помочь?

ответ

1

В то время как вы можете издеваться непосредственно ОРМ и возвращать произвольные результаты, я думаю, было бы более эффективным Вкладыш поддельных данных в базе данных, используя библиотеку, такую ​​как factory_boy и оставить ORM вызывает unmocked:

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 или exclude.

Если вы может легко протестировать его и сохранить ваш тест быстро и эффективно, я думаю, что вы ld избегать насмешек. В вашем случае:

  • Вы можете легко создавать фиктивные данные
  • Ваших тестов будут по-прежнему быстро, если вы используете ORM, потому что Джанго по умолчанию использует в памяти SQLite Databse во время тестирования, избегая ввод/вывод диска.

Mocking мог бы иметь смысл в других ситуациях, например, например, при получении данных по сети с использованием HTTP API.

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 
Смежные вопросы