
Publicado el
Recientemente me encontraba creando clases Test en Salesforce para unas clases que acababa de desarrollar en Apex, en la clase hacia inserción y búsqueda de Attachments con ciertos nombres en específico.
Un método que solo se encargaba de realizar la búsqueda de un Attachment dado un nombre de archivo y un ID del registro al que está ligado dicho Attachment debió de haber sido fácil de testear (5 – 10 minutos máximo), sin embargo, se convirtió en un dolor de cabeza que me llevo bastantes horas de debuggeo.
Como resultado del debuggeo, me encontré con códigos atascados de la sentencia Test.isRunningTest() y en específico me encontré con un trigger que además de estar repleto de dicha sentencia, tenía la siguiente línea de código:
1 | att.Name = Test.isRunningTest() ? 'test' : nombreReal; |
Esa línea de código fue la culpable de mis dolores de cabeza, ya que hacía que no importara como preparara mis datos de prueba en mi clase Test, siempre que se ejecutaba la inserción de mis Attachments de prueba les cambiaba el nombre a test 🥴 y se perdían los nombres reales de mis archivos y por ende no funcionaban mis métodos para hacer Unit Testing.
Claramente la sentencia Test.isRunningTest() se creó para que la podamos usar, pero no hay que abusar de su uso ni mucho menos usarla solo para dar cobertura a nuestras clases, es por esta razón que ese día decidí ponerme a investigar en qué casos si se puede usar y cuando NO se puede ni se debe usar.
El objetivo de hacer Clases Test es probar que los métodos de la clase Apex funcionen como deben funcionar y al llenar nuestros códigos con Test.isRunningTest() se pierde completamente el objetivo de hacer unit test porque solo nos enfocamos en dar cobertura y no en realmente probar el funcionamiento de nuestros desarrollos.
– Se tenía que decir y se dijo 😎
Bueno ya no me enrollo más, vamos al grano, cuando si y cuando NO usar la sentencia Test.isRunningTest()
Cuando se debe usar Test.isRunningTest()
Estos son los 3 escenarios más comunes en donde si o si tenemos que usar la sentencia:
- Simulación de Condiciones Especiales: Para simular condiciones que solo se presentan en entornos productivos y no en entornos de pruebas. Esto permite emular datos o comportamientos de producción que de otra manera serían complejos o casi imposibles de replicar.
- Control de Límites y Llamadas a Servicios Externos: En casos donde la lógica llama a servicios externos o realiza consultas muy costosas. Aquí, Test.isRunningTest() puede usarse para evitar dichas llamadas en las pruebas, mejorando su rendimiento y reduciendo dependencias externas.
- Personalización de Flujos: En situaciones donde parte del código debe ejecutarse solo en producción y omitir la ejecución en el entorno de pruebas. Por ejemplo: lógica que depende de ciertos datos de registros externos o configuraciones que pueden no estar disponibles en el entorno de pruebas.
Como te puedes dar cuenta son escenarios muy muy específicos cuando hay que evitar costes o para evitar la ejecución de funcionalidades que solo deben ejecutarse en un entorno de producción. Asi que por favor, «DI NO AL USO DE Test.isRunningTest()» 😎
Cuando NO se debe usar Test.isRunningTest()
Ahora si vamos con lo interesante, cuando NO usar la sentencia:
- Para Cumplir Requisitos de Cobertura de Código: No se debe usar Test.isRunningTest() para omitir líneas de código con el objetivo de cumplir el umbral de cobertura del 75%. Esto lleva a malas prácticas y puede generar fallos en producción que bien pudieron haberse detectado con la ejecución de los Unit Test.
- Saltarse la Lógica de Negocio: La lógica crítica de la aplicación no debería ignorarse en pruebas mediante Test.isRunningTest(). Esto incluye validaciones, lógica de autorización y reglas comerciales importantes.
- Dependencia en Lógica de Pruebas: No se debe depender de Test.isRunningTest() para establecer comportamientos específicos en las pruebas que no reflejan el funcionamiento real en producción, ya que esto genera inconsistencias.
Justo el problema que tuve fue precisamente porque en los códigos que estuve revisando se usaba Test.isRunningTest() a diestra y siniestra para hacer que la ejecución de las pruebas tuviera un comportamiento especifico que lo llevara a saltarse lógica importante del negocio y para caer en ciertas condiciones para cumplir con el requisito de cobertura, ósea, TODO MAL 🥴
Buenas prácticas al usar Test.isRunningTest()
Si bien no podemos escapar del uso de Test.isRunningTest() existen algunas buenas practicas que podemos seguir:
- Uso Controlado y Comentado: Documentar y justificar su uso para cada caso específico, así queda claro por qué se omite una cierta sección de código en un entorno de prueba.
- Alternativas de Mocking o Datos de Prueba: Siempre que sea posible, es mejor usar datos de prueba configurados en métodos @TestSetup o hacer mocking de datos para simular la lógica de producción sin necesidad de usar Test.isRunningTest().
Bueno ya fue mucho choro, a modo de resumen, la sentencia Test.isRunningTest() es útil para evitar ciertos comportamientos durante las pruebas (o para emular comportamientos o funcionalidades específicas de producción), pero su uso debe estar justificado y NO debe reemplazar la lógica principal ni generar inconsistencias en la aplicación.
P.d. Por si se quedaron intrigados de saber si pude o no solucionar mi problema, les cuento que SI, si pude solucionar mis Unit Test ejecutando un update al nombre de los Attachments después de hacer la inserción en el método donde creo mis datos de prueba.
1 2 3 4 5 6 7 8 | Attachment att = new Attachment(); att.Body = Blob.valueOf( 'Contenido de prueba' ); att.Name = 'Este_Nombre_Realmente_No_Importa.pdf' ; // se renombra a test :S att.ParentId = acc.Id; insert att; att.Name = 'Nombre_Documento.png' ; update att; |
Ahora sí, hasta aquí dejo este artículo. Espero que te sea de utilidad y si consideras que me hizo falta mencionar algo, déjamelo en los comentarios.
¡Nos leemos en el siguiente articulo! 😎
Happy coding! 👨🏻💻