dcramer/mock-django

Mocks returned by QuerySetMock should return self when QuerySet-generating methods are called

boydjj opened this issue · 5 comments

For complicated views, where several QuerySet-generating methods (e.g., filter() or order_by()) are called, the SharedMock returned by QuerySetMock breaks application code. Here's an example::

In [1]: from django.conf import settings; settings.configure()

In [2]: from mock_django.query import QuerySetMock

In [3]: class RouteModel(object): pass

In [4]: route_mgr = QuerySetMock(RouteModel, 1, 2, 3)

In [5]: route_mgr.filter() # this works fine, but it returns a generator

In [6]: route_mgr.filter().order_by() # failure, because generators aren't what we need
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-f57426e07858> in <module>()
----> 1 route_mgr.filter().order_by() # fail

AttributeError: 'generator' object has no attribute 'order_by'

So mocking out a QuerySet with a SharedMock fails in any chain-calling application code.

I must admit I'm not quite clear on why this is happening at the moment, because at least on first glance it seems like self should already be returned. But I'm considering a patch that would ensure that such methods always return self, at least for QuerySetMock. Would such a patch be useful to folks? Is there something I'm missing in my use that would allow me to fix the issue otherwise? And, finally, is there a use case I'm not considering where such a patch would break the current behavior?

I encountered the same problem and managed to solve it, at least for my use case, in 50f7e70

Sorry it's taken a while to get back to this. Your change breaks the existing tests:

$ python runtests.py 
.........................F......
======================================================================
FAIL: Each QuerySet-returning method's return value is unique.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/jboyd/Projects/mock-django/tests/mock_django/query/tests.py", line 27, in test_qs_generator_inequality
    self.assertNotEquals(qs.all(), qs.filter())
AssertionError: <SharedMock name='mock.filter' id='4543475664'> == <SharedMock name='mock.filter' id='4543475664'>

----------------------------------------------------------------------
Ran 32 tests in 0.356s

@henrikalmer If you're currently using your solution, can you take a look at my latest PR and verify that your uses still pass? Instead of just returning a new iterator, I return a new copy of the QuerySetMock for most QuerySet-returning methods.

@boydjj I've tried the latest PR and it seems to work with well, with one exception. I need to be able to add custom queryset returning methods that correspond to methods on a custom manager. I've added a settings variable to do this locally and if you wish I could submit a pull request.

I solved this issue in my code by using mock and return_value:

route_mgr.filter.return_value = Mock()
route_mgr.filter.return_value.order_by = Mock()
route_mgr.filter.return_value.order_by.return_value = QuerySetMock(None)