¡"Isolated" test significa algo muy diferente para distintas personas!
Publicado por Manuel Rivero el 24/01/2026
Este post es una traducción del post original en inglés: “Isolated” test means something very different to different people!.
Introducción.
Un concepto que encontramos muy útil tanto cuando hacemos TDD como cuando introducimos tests en código legacy son las propiedades FIRS (Fast, Isolated, Repeatable, Self-validating), que se utilizan para caracterizar un test unitario ideal. Estas propiedades tienen su origen en el acrónimo FIRST, que caracterizan los tests unitarios ideales en el contexto de TDD, en donde la T significa Timely, (“hecho a tiempo”)[1].
Esto es lo que queremos decir con tests Fast, Isolated, Repeatable y Self-validating:
-
Fast: deberían ejecutarse tan rápido que nunca sintamos la necesidad de ejecutarlos más tarde.
-
Isolated: deberían producir los mismos resultados independientemente del orden en el que se ejecuten. Esto significa que los tests no dependen unos de otros, ni directa ni indirectamente.
-
Repeatable: deberían ser deterministas; sus resultados no deberían cambiar si el comportamiento que probamos y el entorno no han cambiado.
-
Self-validating: deberían pasar o fallar automáticamente, sin requerir que un humano intervenga para determinar el resultado. Esta propiedad es esencial para la automatización de tests.
En el contexto de TDD y en el de añadir tests a código legacy, las propiedades FIRS cumplen roles diferentes, en el primer caso nos pueden servir de guía para diseñar unidades testables, mientras que en el segundo nos ayudan a descubrir problemas de testabilidad[2].
Cuando añadimos tests en un código legacy, las violaciones de las propiedades FIRS resaltan aquellas dependencias que dificultan el testing, conocidas como colaboraciones incómodas (awkward collaborations)[3]. Estas colaboraciones incómodas serían las dependencias que necesitaríamos romper utilizando técnicas de rotura de dependencias[4] para poder introducir tests unitarios.
En el caso de los tests de integración bastaría centrarse en las violaciones de las propiedades Isolated, Repeatable y Self-validating. Las dependencias que violan las propiedades Repeatable y Self-validating requieren de la aplicación de técnicas de rotura de dependencias, mientras que las violaciones de la propiedad Isolated a menudo también pueden resolverse usando otros enfoques, como fixtures específicas para tests o cambios de configuración.
En el contexto de TDD, las violaciones de las propiedades FIRS son una heurística clave para identificar aquellas colaboraciones que necesitamos empujar fuera de la unidad bajo test. Estas colaboraciones incómodas las simularemos con dobles de prueba en nuestros tests unitarios.
Cuando hacemos TDD, identificar colaboraciones incómodas es más difícil porque debemos inferirlas a partir de la especificación. En cambio, en el código legacy son más fáciles de identificar porque las vemos en el código y además se manifiestan a través de los problemas de testabilidad que causan.
Identificar colaboraciones incómodas es, por tanto, una habilidad importante para poder diseñar código testable. En este sentido, las propiedades FIRS resultan una valiosa guía para definir los límites de una unidad, y nos ayudan a desarrollar código testable.
¡Parece que “isolated test” significa algo muy diferente para distintas personas!
Si lees la definición original de isolated en Agile in a Flash: F.I.R.S.T. notarás que es diferente de lo que nosotros hemos expresado en la sección anterior.
Para nosotros isolated significa que “un test debería producir los mismos resultados independientemente del orden en el que se ejecute. Esto significa que los tests no dependen unos de otros de ninguna manera, ni directa ni indirectamente”.
En la definición de isolated de Agile in a Flash: F.I.R.S.T., la flash card afirma:
“Isolated: Failure reasons become obvious.” (Isolated: Las razones del fallo se vuelven evidentes)
Más adelante, en la explicación, elaboran lo que esto significa (el énfasis en negrita fue añadido por nosotros):
Isolated: Tests isolate failures. A developer should never have to reverse-engineer tests or the code being tested to know what went wrong. Each test class name and test method name with the text of the assertion should state exactly what is wrong and where. If a test does not isolate failures, it is best to replace that test with smaller, more-specific tests. (Isolated: Los tests aíslan los fallos. Un desarrollador nunca debería tener que hacer ingeniería inversa de los tests o del código que se está probando para saber qué salió mal. El nombre de cada clase de test y de cada método de test, junto con el texto de la aserción, debería indicar exactamente qué está mal y dónde. Si un test no aísla fallos, es mejor reemplazarlo por tests más pequeños y más específicos.)
A good unit test has a laser-tight focus on a single effect or decision in the system under test. And that system under test tends to be a single part of a single method on a single class (hence “unit”). Tests must not have any order-of-run dependency. They should pass or fail the same way in suite or when run individually. Each suite should be re-runnable even if tests are renamed or reordered randomly. Good tests interfere with no other tests in any way. They impose their initial state without aid from other tests. They clean up after themselves.” (Un buen test unitario se centra de forma extremadamente precisa en un único efecto o decisión en el sistema bajo test. Y ese sistema bajo test suele ser una parte única de un único método en una única clase (de ahí “unit”). El resultado de los tests no debe depender del orden de ejecución. Deberían pasar o fallar de la misma manera al ejecutarse en una suite o de forma individual. Cada suite debería poder ejecutarse de nuevo incluso si los tests se renombran o se reordenan aleatoriamente. Unos tests bien hechos no interfieren con otros tests de ninguna manera. Imponen su estado inicial sin la ayuda de otros tests. Limpian su entorno después de ser ejecutados.”)
Tim Ottinger en FIRST: an idea that ran away from home lo resume así:
“Isolated - tests don’t rely upon either other in any way, including indirectly. Each test isolates one failure mode only.” (Isolated - los tests no dependen unos de otros de ninguna manera, ni siquiera indirectamente. Cada test aísla un único modo de fallo.)[5].
Por contra, para nosotros isolated, en el contexto de identificar colaboraciones incómodas, significa que los tests deberían estar aislados entre sí, lo que, en la práctica, significa que no pueden compartir ningún estado o recurso mutable. Nuestra definición es menos restrictiva que la de Ottinger. Elegimos considerar sólo un aspecto de su definición, que “los tests no interfieren con otros tests de ninguna manera”, y no el otro, “los tests tienen una única razón para fallar” (comentaremos más sobre este otro aspecto más abajo).
Creemos que lo que nosotros entendemos por isolated se alinea con la definición que Kent Beck proporciona en su libro Test Driven Development: By Example. En la sección Isolated Test (página 125) del capítulo Test-Driven Development Patterns, escribe:
“How should the running of tests affect one another? Not at all.” (¿Cómo debería afectar los tests unos a otros al ser ejecutados? Nada en absoluto.)
“[…] the main lesson […] tests should be able to ignore one another completely.” ([…] la lección principal […] los tests deberían poder ignorarse unos a otros completamente.)
“One convenient implication of isolated tests is that the tests are order independent.” (Una implicación conveniente de los tests isolated es que son independientes del orden.)
Además, en su trabajo más reciente Test Desiderata define isolated como:
“tests should return the same results regardless of the order in which they are run”[6] (los tests deberían dar los mismos resultados independientemente del orden en el que se ejecuten).
Dicho esto, hay otra propiedad deseable para los tests en Test Desiderata que resulta interesante para esta discusión, specificity, que Kent Beck explica como:
“Specific: if a test fails, the cause of the failure should be obvious.” (Specific: si un test falla, la causa del fallo debería ser obvia)
Creemos que el segundo aspecto de isolated que aparece en la definición de Ottinger, tener una única razón para fallar, corresponde al nivel más alto posible de especificidad de Beck. Por tanto, parece que lo que Ottinger entiende por isolated estaría incluyendo dos de las propiedades deseables para los tests de las que Beck habla en Test Desiderata:
-
la propiedad de devolver los mismos resultados independientemente del orden en el que se ejecuten (ser isolated).
-
la propiedad de que los fallos de los tests tengan una causa obvia (ser specific).
Tener una única razón para fallar sigue siendo una propiedad altamente deseable que nosotros también tenemos en cuenta al escribir casos de test. Nos puede ayudar tanto a detectar cuando componer comportamientos independientes, como a evitar la sobreespecificación de algunos tests[7].
Sin embargo, en el contexto de identificar colaboraciones incómodas, hemos encontrado que la definición de isolated de Beck es más útil, para evitar caer en la trampa de considerar que la unidad es la clase, y para enseñar el uso de los dobles de prueba como herramientas de aislamiento, el uso principal en el estilo clásico de TDD.
Resumen.
Mostramos cómo las propiedades FIRS pueden resultar valiosas tanto al hacer TDD como al añadir tests en código legacy, porque nos guían a desarrollar código más testable y mantenible.
Exploramos cómo el concepto de tests isolated difiere según el autor. Mientras que la definición de Kent Beck enfatiza la independencia entre tests, asegurando que estos produzcan resultados consistentes independientemente del orden de ejecución, la definición que encontramos en F.I.R.S.T in Agile in a Flash establece además que los tests deberían tener una única razón para fallar. Creemos que esta última definición mezcla la definición de isolation de Beck con otra propiedad de los tests: specificity, ya que tener una única razón para fallar representa el nivel más alto de specificity.
Pensamos que tanto la definición de isolated de Beck como la de Ottinger son valiosas. Sin embargo, la versión de Beck se alinea más estrechamente con lo que nosotros entendemos por isolated en el contexto de identificar colaboraciones incómodas. Para nosotros, la definición de Beck ha demostrado ser especialmente útil para identificar colaboraciones incómodas, evitar caer en la trampa de considerar que la unidad es la clase, y enseñar cómo usar los dobles de prueba como herramientas de aislamiento.
La serie sobre TDD, dobles de prueba y diseño orientado a objetos.
Este post forma parte de una serie sobre TDD, test doubles y diseño orientado a objetos:
-
¡”Isolated” test significa algo muy diferente para distintas personas!.
-
Heuristics to determine unit boundaries: object peer stereotypes, detecting effects and FIRS-ness, (aún por traducir).
-
Breaking out to improve cohesion (peer detection techniques), (aún por traducir).
-
Refactoring the tests after a “Breaking Out” (peer detection techniques), (aún por traducir).
-
Bundling up to reduce coupling and complexity (peer detection techniques), (aún por traducir).
Agradecimientos.
Me gustaría agradecer a Fran Reyes, Emmanuel Valverde Ramos, Fran Iglesias Gómez, Marabesi Matheus y Antonio De La Torre por darme feedback sobre varios borradores de este post.
Finalmente, también me gustaría agradecer a imgflip por su Inconceivable Iñigo Montoya Meme Generator.
Referencias.
-
Unit Tests Are FIRST: Fast, Isolated, Repeatable, Self-Verifying, and Timely, Tim Ottinger y Jeff Langr.
-
Test Desiderata 8/12: Tests Should Be Isolated (from each other), Kent Beck.
-
The class is not the unit in the London school style of TDD, Manuel Rivero.
-
Princess Bride, “You keep using that word. I do not think it means what you think it means.”.
Notas.
[1] Lean también el post “pure unit test” vs. “FIRSTness” de Jay Bazuzi para aprender más sobre la categorización de tests según su FIRSness (eliminando la T), lo cual puede ser útil al trabajar con código legacy o al hacer tests a posteriori.
[2] Profundizamos en este tema en nuestras formaciones de TDD y Changing Legacy Code.
[3] El artículo Mocks Aren’t Stubs de Fowler es el origen del término colaboración incómoda (awkward collaboration).
[4] Lean nuestro post Classifying dependency-breaking techniques.
[5] Además de resumir muy bien qué significa “Isolated” para ellos, FIRST: an idea that ran away from home profundiza en la historia de cómo surgieron las propiedades FIRST y cómo su significado se ha difuminado con el tiempo. Además, incluye una lista de fuentes que discuten FIRST, destacando los cambios realizados por cada fuente, cómo en el juego del teléfono roto, y señalando que algunas ni siquiera dan crédito a los autores originales.
También hace referencia a otro artículo publicado por Pragmatic Programmers como el origen del acrónimo Unit Tests Are FIRST: Fast, Isolated, Repeatable, Self-Verifying, and Timely, que creemos que explica FAST aún mejor y ademas incluye ejemplos de código.
Finalmente, explica el valor de escribir los tests primero y critica escribirlos después.
[6] Ian Cooper, en su charla TDD, where did it all go wrong, afirma que:
“For Kent Beck, [a unit test] is a test that runs in isolation from other tests.” (Para Kent Beck, [un test unitario] es un test que se ejecuta de forma aislada respecto a otros tests.)
“[…] NOT to be confused with the classical unit test definition of targeting a module.” ([…] NO debe confundirse con la definición clásica de test unitario que apunta a un módulo.)
”A lot of issues with TDD is people misunderstanding isolation as class isolation […]” (Muchos de los problemas al hacer TDD provienen de que la gente malinterpreta el aislamiento como aislar la clase […])
Hablamos de este malentendido frecuente, considerar que la unidad es la clase, en nuestro post La clase no es la unidad en el estilo de TDD de Londres.