Python SQLAlchemy: simulando el método "desc" de un atributo modelo
unit-testing mocking (1)
No creo que realmente esté obteniendo muchos beneficios al usar simulaciones para probar sus consultas. Las pruebas deberían probar la lógica del código, no la implementación . Una solución mejor sería crear una nueva base de datos, agregarle algunos objetos, ejecutar la consulta en esa base de datos y determinar si está obteniendo los resultados correctos. Por ejemplo:
# Create the engine. This starts a fresh database
engine = create_engine(''sqlite://'')
# Fills the database with the tables needed.
# If you use declarative, then the metadata for your tables can be found using Base.metadata
metadata.create_all(engine)
# Create a session to this database
session = sessionmaker(bind=engine)()
# Create some posts using the session and commit them
...
# Test your repository object...
repo = PostRepository(session)
results = repo.find_latest()
# Run your assertions of results
...
Ahora, en realidad estás probando la lógica del código. Esto significa que puede cambiar la implementación de su método, pero siempre que la consulta funcione correctamente, las pruebas deben pasar. Si lo desea, puede escribir este método como una consulta que obtiene todos los objetos, luego divide la lista resultante. La prueba pasaría, como debería. Más adelante, podría cambiar la implementación para ejecutar la consulta utilizando las API de expresión SA, y la prueba pasaría.
Una cosa a tener en cuenta es que puede tener problemas con el comportamiento de sqlite de forma diferente a otro tipo de base de datos. El uso de sqlite en la memoria le proporciona pruebas rápidas, pero si quiere tomar en serio estas pruebas, probablemente también desee ejecutarlas en el mismo tipo de base de datos que utilizará en la producción.
En mi aplicación, hay una clase para cada modelo que contiene consultas de uso común (supongo que es algo así como un "Repositorio" en el lenguaje DDD). Cada una de estas clases se pasa al objeto de sesión SQLAlchemy para crear consultas durante la construcción. Tengo un poco de dificultad para determinar la mejor forma de afirmar que ciertas consultas se están ejecutando en mis pruebas unitarias. Utilizando el ejemplo de blog ubicuo, digamos que tengo un modelo "Publicar" con columnas y atributos "fecha" y "contenido". También tengo un "PostRepository" con el método "find_latest" que se supone que consulta todas las publicaciones en orden descendente por "fecha". Se ve algo así como:
from myapp.models import Post
class PostRepository(object):
def __init__(self, session):
self._s = session
def find_latest(self):
return self._s.query(Post).order_by(Post.date.desc())
Tengo problemas para burlarme de la llamada Post.date.desc (). En este momento soy mono parcheando un simulacro de Post.date.desc en mi prueba de unidad, pero creo que es probable que haya un mejor enfoque.
Editar: Estoy usando mox para objetos falsos, mi prueba de unidad actual se ve algo así como:
import unittest
import mox
class TestPostRepository(unittest.TestCase):
def setUp(self):
self._mox = mox.Mox()
def _create_session_mock(self):
from sqlalchemy.orm.session import Session
return self._mox.CreateMock(Session)
def _create_query_mock(self):
from sqlalchemy.orm.query import Query
return self._mox.CreateMock(Query)
def _create_desc_mock(self):
from myapp.models import Post
return self._mox.CreateMock(Post.date.desc)
def test_find_latest(self):
from myapp.models.repositories import PostRepository
from myapp.models import Post
expected_result = ''test''
session_mock = self._create_session_mock()
query_mock = self._create_query_mock()
desc_mock = self._create_desc_mock()
# Monkey patch
tmp = Post.date.desc
Post.date.desc = desc_mock
session_mock.query(Post).AndReturn(query_mock)
query_mock.order_by(Post.date.desc().AndReturn(''test'')).AndReturn(query_mock)
query_mock.offset(0).AndReturn(query_mock)
query_mock.limit(10).AndReturn(expected_result)
self._mox.ReplayAll()
r = PostRepository(session_mock)
result = r.find_latest()
self._mox.VerifyAll()
self.assertEquals(expected_result, result)
Post.date.desc = tmp
Esto funciona, aunque se siente feo y no estoy seguro de por qué falla sin el "AndReturn (''test'')" pieza de "Post.date.desc (). AndReturn (''test'')"