Odoo · Testing · Calidad
Un test que pasa siempre, incluso cuando rompes el código que debería cubrir, es peor que no tener test: da una falsa sensación de seguridad. En Odoo, la mayoría de falsos positivos vienen de preparar mal los datos de prueba, no de un mal assert.
TransactionCase hace rollback automático: cada test empieza en un estado limpio.La base
TransactionCase: aislamiento real entre tests
Cada método de test en TransactionCase corre dentro de una transacción que se revierte al terminar. Esto significa que un test nunca debería depender del orden de ejecución ni dejar basura para el siguiente: si lo hace, es síntoma de que está asumiendo datos que no creó él mismo.
# tests/test_documento.py
from odoo.tests.common import TransactionCase
class TestDocumento(TransactionCase):
def setUp(self):
super().setUp()
self.partner = self.env["res.partner"].create({"name": "Cliente Test"})
def test_confirmar_documento(self):
doc = self.env["mi_modulo.documento"].create({"partner_id": self.partner.id})
doc.action_confirm()
self.assertEqual(doc.state, "confirmed")
El error más común
No dependas de datos demo ni de env.ref() a ciegas
Un test que hace self.env.ref("base.res_partner_1") asume que ese dato demo existe en la base de datos de test. Si otro módulo se instala sin datos demo, o el external ID cambia, el test falla por una razón ajena a lo que pretende validar. Crear los datos propios en setUp() es más verboso pero mucho más fiable.
# Frágil: depende de que ese dato demo exista con ese ID exacto
partner = self.env.ref("base.res_partner_1")
# Fiable: el test controla exactamente los datos que usa
partner = self.env["res.partner"].create({"name": "Cliente Test"})
env.ref() datos que tu propio módulo garantiza (grupos de seguridad, secuencias) sí es válido; el problema es depender de datos demo de módulos ajenos.
Cuándo mockear
Mocks solo en el borde del sistema, nunca en el ORM
Mockear una llamada a una API externa (pasarela de pago, servicio de envío de SMS) es correcto: no quieres que tus tests dependan de un tercero. Mockear el propio ORM de Odoo (search, create) para "que el test pase rápido" es una señal de alarma: dejas de probar el comportamiento real y empiezas a probar tus propias suposiciones.
from unittest.mock import patch
class TestNotificacion(TransactionCase):
@patch("mi_modulo.models.documento.requests.post")
def test_envio_sms_falla_no_rompe_confirmacion(self, mock_post):
mock_post.side_effect = ConnectionError("timeout")
doc = self.env["mi_modulo.documento"].create({"partner_id": self.partner.id})
doc.action_confirm() # no debe lanzar aunque el SMS falle
self.assertEqual(doc.state, "confirmed")
| Situación | Qué hacer |
|---|---|
| Llamada a API externa (pago, SMS, email) | Mock del cliente HTTP, nunca del ORM |
| Datos necesarios para el test | Crearlos en setUp(), no asumir demo |
| Referencia a dato garantizado por tu propio módulo | env.ref() es válido |
| Verificar reglas de acceso | with_user() + assertRaises(AccessError) |
Resumen
TransactionCase te da aislamiento gratis; aúnalo con datos creados explícitamente en cada test y mocks reservados al borde del sistema. Un test que depende de datos demo ajenos o que mockea el ORM no te protege de nada: solo aparenta cobertura.