[Answered ]-Patching multiple ORM calls in Django

1👍

While you can mock directly the ORM and return arbitrary results, I think it would be more efficient an insert fake data in the database using a library such as factory_boy and leave the ORM calls 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)

At some point, you cannot mock everything, and if you mock ORM calls, you may end up with passing tests while your code is actually buggy (for example if you have a mistake in your filter or exclude statements.

If you can test it easily, and keep your test fast and efficient, I think you should avoid mocking something. In your case:

  • You can easily create dummy data
  • Your tests will still be fast if you use the ORM because Django by default uses an in-memory SQLite databse during tests, avoiding disk I/O.

Mocking would totally make sense in other situations though, such as retrieving data over the network using an HTTP API, for example.

👤Agate

1👍

In our project, we’re trying to decide when it’s a good idea to mock out the ORM layer and when it isn’t. Mocking the ORM layer makes the tests run way faster, but the tests are a bit more brittle.

To me, it seems like it’s not worth mocking the ORM if there’s any complexity in the queries. You should test complex queries against a test database with some sample data in it.

If you do have simple queries that you want to mock out, try the django-mock-queries package. I contributed a decorator called mocked_relations that mocks out the stuff you’re asking about: Event.objects and foreign key relations.

Take a look at the example that shows how to use it. You can list several model classes that you want to mock out.

@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

Leave a comment