Mocking a Django Queryset in order to test a function that takes a queryset

前端 未结 9 1237
青春惊慌失措
青春惊慌失措 2020-12-28 13:55

I have a utility function in my Django project, it takes a queryset, gets some data from it and returns a result. I\'d like to write some tests for this function. Is there a

相关标签:
9条回答
  • 2020-12-28 14:51

    For an empty Queryset, I'd go simply for using none as keithhackbarth has already stated.

    However, to mock a Queryset that will return a list of values, I prefer to use a Mock with a spec of the Model's manager. As an example (Python 2.7 style - I've used the external Mock library), here's a simple test where the Queryset is filtered and then counted:

    from django.test import TestCase
    from mock import Mock
    
    from .models import Example
    
    
    def queryset_func(queryset, filter_value):
        """
        An example function to be tested
        """
        return queryset.filter(stuff=filter_value).count()
    
    
    class TestQuerysetFunc(TestCase):
    
        def test_happy(self):
            """
            `queryset_func` filters provided queryset and counts result
            """
            m_queryset = Mock(spec=Example.objects)
            m_queryset.filter.return_value = m_queryset
            m_queryset.count.return_value = 97
    
            result = func_to_test(m_queryset, '__TEST_VALUE__')
    
            self.assertEqual(result, 97)
            m_queryset.filter.assert_called_once_with(stuff='__TEST_VALUE__')
            m_queryset.count.assert_called_once_with()
    

    However, to fulfil the question, instead of setting a return_value for count, this could easily be adjusted to be a list of model instances returned from all.

    Note that chaining is handled by setting the filter to return the mocked queryset:

    m_queryset.filter.return_value = m_queryset
    

    This would need to be applied for any queryset methods used in the function under test, e.g. exclude, etc.

    0 讨论(0)
  • 2020-12-28 14:54

    For this I use Django's .none() function.

    For example:

    class Location(models.Model):
      name = models.CharField(max_length=100)
    mock_locations = Location.objects.none()
    

    This is the method used frequently in Django's own internal test cases. Based on comments in the code

    Calling none() will create a queryset that never returns any objects and no
    +query will be executed when accessing the results. A qs.none() queryset
    +is an instance of ``EmptyQuerySet``.
    
    0 讨论(0)
  • 2020-12-28 14:59

    You can mock like this:

    @patch('django.db.models.query.QuerySet')
    def test_returning_distinct_records_for_city(self, mock_qs):
        self.assertTrue(mock_qs.called)
    
    0 讨论(0)
提交回复
热议问题