Burlarse de un Django Queryset para probar una función que toma un queryset

Tengo una función de utilidad en mi proyecto Django, toma un queryset, obtiene algunos datos de él y devuelve un resultado. Me gustaría escribir algunas pruebas para esta función. ¿Hay alguna forma de “burlarse” de un QuerySet? Me gustaría crear un objeto que no toque la base de datos, y puedo proporcionarle una lista de valores para usar (es decir, algunas filas falsas) y luego actuará como un queryset, y permitirá a alguien hacer búsquedas de campo en él / filtrar / obtener / todo etc.

¿Ya existe algo como esto?

No que yo sepa, pero ¿por qué no usar un queryset real? El marco de prueba está configurado para permitirle crear datos de muestra dentro de su prueba, y la base de datos se vuelve a crear en cada prueba, por lo que no parece haber ninguna razón para no usar el objeto real.

Por supuesto que puedes burlarte de un QuerySet, puedes burlarte de cualquier cosa.

Puede crear un objeto usted mismo, y darle la interfaz que necesita, y hacer que devuelva los datos que desee. En el fondo, la burla no es más que proporcionar un “doble de prueba” que actúa lo suficiente como para los propósitos de tus pruebas.

La forma de comenzar de baja tecnología es definir un objeto:

class MockQuerySet(object): pass 

luego crea uno de estos y entrégalo a tu prueba. La prueba fallará, probablemente en un AttributeError . Eso le dirá lo que necesita implementar en su MockQuerySet . Repita hasta que su objeto sea lo suficientemente rico para sus pruebas.

Estoy teniendo el mismo problema, y ​​parece que una buena persona ha escrito una biblioteca para burlarse de QuerySets, se llama mock-django y el código específico que necesitará está aquí https://github.com/dcramer/mock-django /blob/master/mock_django/query.py ¡Creo que luego puede parchear la función de objetos de modelos para devolver uno de estos objetos QuerySetMock que ha configurado para devolver algo esperado!

Para un Queryset vacío, iría simplemente para usar none como keithhackbarth ya ha indicado .

Sin embargo, para simular un Queryset que devolverá una lista de valores, prefiero usar un simulacro con una spec del administrador del modelo. Como ejemplo (estilo Python 2.7 – He usado la biblioteca de simulacros externa ), aquí hay una prueba simple donde se filtra el Queryset y luego se cuenta:

 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() 

Sin embargo, para cumplir con la pregunta, en lugar de establecer un valor de return_value para el count , esto podría ajustarse fácilmente para que sea una list de instancias de modelo devueltas por all .

Tenga en cuenta que el encadenamiento se maneja configurando el filter para devolver el queryset simulado:

 m_queryset.filter.return_value = m_queryset 

Esto debería aplicarse a cualquier método de conjunto de consultas utilizado en la función bajo prueba, por ejemplo, exclude , etc.

Para esto uso la función .none () de Django .

Por ejemplo:

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

Este es el método utilizado frecuentemente en los casos de prueba internos de Django. Basado en comentarios en el código.

 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``. 

Pruebe la biblioteca django_mock_queries que le permite django_mock_queries el acceso a la base de datos y seguir utilizando algunas de las características del conjunto de consultas de Django, como el filtrado.

Revelación completa: aporté algunas características al proyecto.

¿Has mirado en FactoryBoy? https://factoryboy.readthedocs.io/en/latest/orms.html Es una herramienta de reemplazo de dispositivos con soporte para django orm; las fábricas básicamente generan objetos similares a orm (ya sea en la memoria o en una base de datos de prueba).

Aquí hay un gran artículo para comenzar: https://www.caktusgroup.com/blog/2013/07/17/factory-boy-alternative-django-testing-fixtures/

Te puedes burlar así:

 @patch('django.db.models.query.QuerySet') def test_returning_distinct_records_for_city(self, mock_qs): self.assertTrue(mock_qs.called) 

Un primer consejo sería dividir la función en dos partes, una que crea el conjunto de consultas y otra que manipula la salida de la misma. De esta manera, probar la segunda parte es sencillo.

Para el problema de la base de datos, investigué si django usa sqlite-in-memory y descubrí que la versión reciente de django usa la base de datos sqlite -in-memory, de la página de prueba de django unit :

Cuando se usa el motor de base de datos SQLite, las pruebas usarán por defecto una base de datos en memoria (es decir, la base de datos se creará en la memoria, ¡evitando el sistema de archivos por completo!).

Simular el objeto QuerySet no hará que ejerza su lógica completa.