2008-10-24 11 views
52

What Makes a Good Unit Test? dice que una prueba solo debe probar una cosa. ¿Cuál es el beneficio de eso?¿Por qué deberían las pruebas unitarias probar solo una cosa?

¿No sería mejor escribir pruebas un poco más grandes que prueban bloques más grandes de código? Investigar una falla en la prueba es de todos modos difícil y no veo ayuda en pruebas más pequeñas.

Editar: La unidad de palabra no es tan importante. Digamos que considero la unidad un poco más grande. Ese no es el problema aqui. La verdadera pregunta es por qué hacer una prueba o más para todos los métodos, ya que pocas pruebas que cubren muchos métodos son más simples.

Un ejemplo: una clase de lista. ¿Por qué debería hacer pruebas separadas para agregar y eliminar? Una prueba que primero agrega luego elimina los sonidos más simples.

+0

Bueno, es posible que no detecte un error en el código que solo ocurre cuando se agrega y no se elimina. –

+0

Porque si prueba varias cosas, se llamaría prueba de plétora. – tchen

+0

La respuesta a "¿Crees que las pruebas unitarias son la bomba?" por lo general se reduce a la pregunta "¿Qué tan bueno eres en los simulacros y la arquitectura del código?". Si no puedes dividir tu código en unidades individuales para probar (simula las entradas y salidas, y solo ejecutas el código que estás probando), entonces las pruebas unitarias simplemente no encajarán. Te encontrarás escribiendo las mismas configuraciones/desmantelamientos una y otra vez y tardarán una eternidad en ejecutarse. – TheGrimmScientist

Respuesta

59

Voy a dar un paso aquí y decir que el consejo de "solo probar una cosa" no es tan útil como a veces se dice.

A veces, las pruebas requieren una cierta cantidad de configuración. A veces incluso pueden tomar una cierta cantidad de tiempo para configurar (en el mundo real). A menudo puedes probar dos acciones de una vez.

Pro: solo tienen toda esa configuración una vez. Tus pruebas después de la primera acción demostrarán que el mundo es como esperas que sea antes de la segunda acción. Menos código, prueba más rápida.

Con: si bien falla la acción, obtendrá el mismo resultado: la misma prueba fallará. Tendrá menos información sobre dónde está el problema que si solo tuviera una sola acción en cada una de las dos pruebas.

En realidad, me parece que la "estafa" aquí no es un gran problema. La traza de pila a menudo reduce las cosas muy rápidamente, y me aseguraré de arreglar el código de todos modos.

Una "con" ligeramente diferente aquí es que rompe el ciclo "escribir una nueva prueba, hacerla pasar, refactorizar". Veo eso como un ciclo ideal, pero uno que no siempre refleja la realidad. A veces es simplemente más pragmático agregar una acción adicional y verificar (o posiblemente solo otro cheque a una acción existente) en una prueba actual que crear una nueva.

+4

Como siempre Jon, es posible que estés en una extremidad, pero estás hablando sentido de esa rama que elegiste como tu percha. –

+1

Estoy de acuerdo con su punto: si bien una buena práctica puede ser probar solo una característica por prueba, su entorno puede dictar que pruebe varias características. –

+5

Downvoters: Los comentarios son útiles ... –

70

Al probar solo una cosa, se aislará una sola cosa y se demostrará si funciona o no. Esa es la idea con pruebas unitarias. No hay nada malo con las pruebas que prueban más de una cosa, pero eso generalmente se conoce como pruebas de integración. Ambos tienen méritos, basados ​​en el contexto.

Para usar un ejemplo, si su lámpara de noche no se enciende, y reemplaza la bombilla y cambia el cable de extensión, no sabe qué cambio solucionó el problema. Debería haber hecho las pruebas unitarias y ha separado sus preocupaciones para aislar el problema.

+0

¿Por qué es importante saber todo a la vez? Puedo arreglar una falla y luego ejecutar la prueba nuevamente para obtener la siguiente. – iny

+1

La prueba de "unidad", por definición, prueba una unidad de su programa (es decir, una pieza) a la vez. – wprl

+1

Absolutamente, puede hacerlo de esa manera si le funciona. No me conceden fácilmente las metodologías. Solo hago lo que funciona en el contexto. – MrBoJangles

11

Generalmente, no se recomiendan las pruebas que verifican más de una cosa, ya que están más estrechamente acopladas y quebradizas. Si cambia algo en el código, tomará más tiempo cambiar la prueba, ya que hay más cosas para tener en cuenta.

[Editar:] Ok, decir que este es un método de ensayo de la muestra:

[TestMethod] 
public void TestSomething() { 
    // Test condition A 
    // Test condition B 
    // Test condition C 
    // Test condition D 
} 

Si la prueba de condición A falla, entonces B, C, y D aparecerá a fallar también, y ganó No te proporciono ninguna utilidad. ¿Qué pasa si el cambio de código hubiera causado que C también fallara? Si los hubieras dividido en 4 pruebas separadas, sabrías esto.

+1

Pero escribir pruebas más pequeñas también lleva más tiempo ya que uno tiene que escribir más código para configurarlo. No puedes borrar sin crear algo. ¿Por qué no crear y luego eliminar en la misma prueba? – iny

+0

Estoy confundido, ¿qué es exactamente "creado" y "eliminado" aquí? Mi experiencia me dice que cuando tengo pruebas largas y monolíticas, paso más tiempo depurándolas que el código que prueban. – swilliams

+0

Esta es una buena discusión, y me gusta que estés defendiendo tu opinión, incluso si creo que estás equivocado :) – swilliams

2

Si prueba más de una cosa y lo primero que falla es que no sabrá si las siguientes cosas que está probando pasan o no. Es más fácil de arreglar cuando sabes todo lo que fallará.

11

Haaa ... pruebas unitarias.

Empuje cualquier "directiva" demasiado lejos y se vuelve inutilizable rápidamente.

prueba de prueba de una sola unidad, una sola cosa es tan buena práctica como un solo método hace una sola tarea. Pero en mi humilde opinión, eso no significa que una sola prueba solo contenga una sola afirmación.

Es

@Test 
public void checkNullInputFirstArgument(){...} 
@Test 
public void checkNullInputSecondArgument(){...} 
@Test 
public void checkOverInputFirstArgument(){...} 
... 

mejor que

@Test 
public void testLimitConditions(){...} 

es cuestión de gusto en mi opinión, en lugar de las buenas prácticas. Yo personalmente prefiero este último.

Pero

@Test 
public void doesWork(){...} 

es en realidad lo que la "instrucción" quiere que evitar a toda costa y lo drena mi cordura el más rápido.

Como conclusión final, agrupe las cosas que están semánticamente relacionadas y que se pueden probar fácilmente juntas, de modo que un mensaje de prueba fallido, en sí mismo, es lo suficientemente significativo como para ir directamente al código.

Regla de oro aquí en un informe de prueba fallido: si primero tiene que leer el código de la prueba, entonces su prueba no está estructurada lo suficientemente bien y necesita más división en pruebas más pequeñas.

Mis 2 centavos.

+0

Si el marco de prueba puede identificar la ubicación de la falla en una prueba con múltiples aserciones, eso ayuda mucho a aliviar la restricción de las pruebas unitarias. Realmente puedo ir aquí en lo que respecta a los ejemplos anteriores. – MrBoJangles

+0

"Prueba de prueba de unidad única, una sola cosa es tan buena práctica como un solo método hace una sola tarea". Es curioso que digas eso. Porque necesita tener funciones/códigos muy limpios para poder realizar buenas pruebas. – TheGrimmScientist

3

La respuesta GLib, pero afortunadamente útil, es esa unidad = uno. Si prueba más de una cosa, entonces no está realizando pruebas unitarias.

6

Al usar el desarrollo basado en pruebas, primero debe escribir las pruebas y luego escribir el código para pasar la prueba. Si sus pruebas están enfocadas, esto hace que escribir el código para pasar la prueba sea más fácil.

Por ejemplo, podría tener un método que tome un parámetro. Una de las cosas en las que podría pensar primero es, ¿qué debería pasar si el parámetro es nulo? Debería lanzar una excepción ArgumentNull (creo). Así que escribo una prueba que verifica si se lanza esa excepción cuando paso un argumento nulo. Ejecute la prueba. De acuerdo, arroja NotImplementedException. Voy y lo soluciono al cambiar el código para lanzar una excepción ArgumentNull. Ejecuta mi prueba que pasa. Entonces pienso, ¿qué pasa si es demasiado pequeño o demasiado grande? Ah, son dos pruebas. Yo escribo el caso demasiado pequeño primero.

El punto es que no pienso en el comportamiento del método todo a la vez. Lo construyo de forma incremental (y lógica) pensando en lo que debería hacer, luego implemento el código y la refactorización a medida que avanzo para que se vea bonito (elegante). Esta es la razón por la cual las pruebas deben ser pequeñas y enfocadas porque cuando se piensa en el comportamiento se debe desarrollar en incrementos pequeños y comprensibles.

+0

Esta es una gran respuesta. Las pruebas unitarias ayudan al desarrollo impulsado por pruebas. Ese es un excelente argumento para pruebas unitarias. – MrBoJangles

+0

Realmente no había pensado, pero sí. Probar solo una cosa (o cosas pequeñas) hace posible TDD.Si sus pruebas fueron grandes, TDD sería una forma abismal de escribir software. – tvanfosson

3

Una prueba de unidad más pequeña deja más claro dónde está el problema cuando fallan.

2

En cuanto a su ejemplo: Si está probando agregar y eliminar en la misma prueba de unidad, ¿cómo verifica que el elemento se haya agregado alguna vez a su lista? Es por eso que necesita agregar y verificar que fue agregado en una prueba.

O para usar el ejemplo de la lámpara: Si desea probar su lámpara y todo lo que hace es encender y apagar el interruptor, ¿cómo sabe que la lámpara se ha encendido alguna vez? Debe tomar el paso intermedio para mirar la lámpara y verificar que esté encendida. Luego puede apagarlo y verificar que se apagó.

+0

Es más fácil agregar afirmar entre. – iny

4

Tener pruebas que verifiquen solo una cosa facilita la solución de problemas. No quiere decir que tampoco deba tener pruebas que prueben varias cosas, o múltiples pruebas que compartan la misma configuración/desmontaje.

Aquí debería haber un ejemplo ilustrativo. Digamos que usted tiene una clase de pila con consultas:

  • getSize
  • estaVacia
  • getTop

y métodos para mutar la pila

  • empuje (unObjeto)
  • pop()

Ahora, consideremos el siguiente caso de prueba para que (estoy usando Python como pseudo-código para este ejemplo.)

class TestCase(): 
    def setup(): 
     self.stack = new Stack() 
    def test(): 
     stack.push(1) 
     stack.push(2) 
     stack.pop() 
     assert stack.top() == 1, "top() isn't showing correct object" 
     assert stack.getSize() == 1, "getSize() call failed" 

A partir de este caso de prueba, se puede determinar si algo está mal, pero no si está aislado en las implementaciones push() o pop(), o en las consultas que devuelven valores: top() y getSize().

Si agregamos casos de prueba individuales para cada método y su comportamiento, las cosas se vuelven mucho más fáciles de diagnosticar. Además, al hacer una configuración nueva para cada caso de prueba, podemos garantizar que el problema está completamente dentro de los métodos que el método de prueba fallido llamó.

def test_size(): 
    assert stack.getSize() == 0 
    assert stack.isEmpty() 

def test_push(): 
    self.stack.push(1) 
    assert stack.top() == 1, "top returns wrong object after push" 
    assert stack.getSize() == 1, "getSize wrong after push" 

def test_pop(): 
    stack.push(1) 
    stack.pop() 
    assert stack.getSize() == 0, "getSize wrong after push" 

En lo que se refiere al desarrollo basado en pruebas. Personalmente escribo "pruebas funcionales" más grandes que terminan probando múltiples métodos al principio, y luego creo pruebas unitarias a medida que comienzo a implementar piezas individuales.

Otra forma de verlo son las pruebas unitarias que verifican el contrato de cada método individual, mientras que las pruebas más grandes verifican el contrato que deben seguir los objetos y el sistema en su conjunto.

Todavía estoy usando tres llamadas a métodos en test_push, sin embargo, tanto top() como getSize() son consultas que se prueban con métodos de prueba separados.

Puede obtener una funcionalidad similar al agregar más afirmaciones a la prueba individual, pero luego se ocultarán las fallas de afirmación posteriores.

+0

En primer lugar, me parece que está probando tres métodos en test_push, no uno, y aún tiene que ver qué afirmación no logró determinar qué es lo que está mal. Y estas dos pruebas no prueban tanto comportamiento como la prueba combinada original. Entonces, ¿por qué no la prueba combinada con más afirmaciones? – Sol

+0

Ver publicación para una explicación extendida. – Ryan

2

Apoyo la idea de que las pruebas unitarias solo deberían probar una cosa. También me desvío un poco. Hoy tuve una prueba en la que la instalación costosa parecía forzarme a hacer más de una afirmación por prueba.

namespace Tests.Integration 
{ 
    [TestFixture] 
    public class FeeMessageTest 
    { 
    [Test] 
    public void ShouldHaveCorrectValues 
    { 
     var fees = CallSlowRunningFeeService(); 
     Assert.AreEqual(6.50m, fees.ConvenienceFee); 
     Assert.AreEqual(2.95m, fees.CreditCardFee); 
     Assert.AreEqual(59.95m, fees.ChangeFee); 
    } 
    } 
} 

Al mismo tiempo, realmente quería ver todas mis afirmaciones que fallaron, no solo la primera. Esperaba que todos fallaran, y necesitaba saber qué cantidades realmente estaba recuperando. Pero, un estándar [SetUp] con cada prueba dividida causaría 3 llamadas al servicio lento. De repente, recordé un artículo que sugería que el uso de construcciones de prueba "no convencionales" es donde se oculta la mitad del beneficio de las pruebas unitarias. (Creo que fue una publicación de Jeremy Miller, pero no puedo encontrarla ahora.) De repente me vino a la mente [TestFixtureSetUp], y me di cuenta de que podía hacer una única llamada de servicio, pero aún tenía métodos de prueba expresivos por separado.

namespace Tests.Integration 
{ 
    [TestFixture] 
    public class FeeMessageTest 
    { 
    Fees fees; 
    [TestFixtureSetUp] 
    public void FetchFeesMessageFromService() 
    { 
     fees = CallSlowRunningFeeService(); 
    } 

    [Test] 
    public void ShouldHaveCorrectConvenienceFee() 
    { 
     Assert.AreEqual(6.50m, fees.ConvenienceFee); 
    } 

    [Test] 
    public void ShouldHaveCorrectCreditCardFee() 
    { 
     Assert.AreEqual(2.95m, fees.CreditCardFee); 
    } 

    [Test] 
    public void ShouldHaveCorrectChangeFee() 
    { 
     Assert.AreEqual(59.95m, fees.ChangeFee); 
    } 
    } 
} 

hay más código en esta prueba, pero ofrece mucho más valor al mostrarme todos los valores que no coinciden con las expectativas a la vez.

Un colega también señaló que esto es un poco como specunit.net de Scott Bellware: http://code.google.com/p/specunit-net/

1

Otra desventaja práctica de las pruebas unitarias muy granular es que rompe el DRY principle. He trabajado en proyectos donde la regla era que cada método público de una clase tenía que tener una prueba de unidad (a [TestMethod]). Obviamente, esto agregó un poco de sobrecarga cada vez que creaste un método público, pero el verdadero problema fue que agregó algo de "fricción" a la refactorización.

Es similar a la documentación de nivel de método, es bueno tenerlo, pero es otra cosa que debe mantenerse y hace que cambiar la firma o el nombre sea un poco más engorroso y ralentiza la refactorización de hilo (como se describe en "Refactoring Tools: Fitness for Purpose" por Emerson Murphy-Hill y Andrew P. Black. PDF, 1.3 MB).

Al igual que la mayoría de las cosas en el diseño, hay una desventaja de que la frase "una prueba debería probar solo una cosa" no captura.

+1

¿Los métodos privados no deberían someterse a pruebas unitarias? – txwikinger

7

Piense en construir un automóvil. Si tuviera que aplicar su teoría, simplemente probar grandes cosas, entonces ¿por qué no hacer una prueba para conducir el automóvil a través del desierto? Se descompone. Bien, entonces dime qué causó el problema. No puedes. Esa es una prueba de escenario.

Una prueba de funcionamiento puede ser encender el motor. Falla. Pero eso podría deberse a una serie de razones. Aún no podías decirme exactamente qué fue lo que causó el problema. Sin embargo, nos estamos acercando.

Una prueba unitaria es más específica, y en primer lugar identificará dónde se rompe el código, pero también (si se usa TDD) ayudará a diseñar su código en fragmentos transparentes y modulares.

Alguien mencionó sobre el uso del seguimiento de la pila. Olvídalo. Ese es un segundo recurso. Pasar por el seguimiento de la pila o usar la depuración es un problema y puede llevar mucho tiempo. Especialmente en sistemas más grandes, y errores complejos.

buenas características de una prueba de unidad:

  • Fast (milisegundos)
  • independientes. No se ve afectado ni depende de otras pruebas
  • Clear. No debe estar hinchado o contener una gran cantidad de configuración.
+0

Esta debería ser la respuesta aceptada. –

1

Cuando falla una prueba, hay tres opciones:

  1. La aplicación está roto y debe ser reparado.
  2. La prueba está rota y se debe corregir.
  3. La prueba ya no se necesita y se debe eliminar.

pruebas de grano fino con nombres descriptivos ayudan al lector a saber qué fue escrito la prueba, que a su vez hace que sea más fácil saber cuál de las opciones anteriores para elegir. El nombre de la prueba debe describir el comportamiento que se especifica en la prueba, y solo un comportamiento por prueba, de modo que con solo leer los nombres de las pruebas el lector sabrá qué hace el sistema. Consulte this article para obtener más información.

Por otro lado, si una prueba está haciendo muchas cosas diferentes y tiene un nombre no descriptivo (como pruebas nombradas después de los métodos en la implementación), entonces será muy difícil averiguar la motivación detrás la prueba, y será difícil saber cuándo y cómo cambiar la prueba.

Aquí es lo que una puede ser similar (con GoSpec), cuando cada prueba pruebas sólo una cosa:

func StackSpec(c gospec.Context) { 
    stack := NewStack() 

    c.Specify("An empty stack", func() { 

    c.Specify("is empty", func() { 
     c.Then(stack).Should.Be(stack.Empty()) 
    }) 
    c.Specify("After a push, the stack is no longer empty", func() { 
     stack.Push("foo") 
     c.Then(stack).ShouldNot.Be(stack.Empty()) 
    }) 
    }) 

    c.Specify("When objects have been pushed onto a stack", func() { 
    stack.Push("one") 
    stack.Push("two") 

    c.Specify("the object pushed last is popped first", func() { 
     x := stack.Pop() 
     c.Then(x).Should.Equal("two") 
    }) 
    c.Specify("the object pushed first is popped last", func() { 
     stack.Pop() 
     x := stack.Pop() 
     c.Then(x).Should.Equal("one") 
    }) 
    c.Specify("After popping all objects, the stack is empty", func() { 
     stack.Pop() 
     stack.Pop() 
     c.Then(stack).Should.Be(stack.Empty()) 
    }) 
    }) 
} 
+0

Downvoters - ¿comentarios? –

+0

La diferencia aquí es que efectivamente has anidado las pruebas. Las tres pruebas sobre "empujar el último se reventa primero", "empujar primero se hace estallar al último" y "después la pila está vacía" son subtests efectivamente. Esa es una manera bastante ordenada de hacer las cosas, pero no es compatible con (digamos) JUnit y NUnit. (No me gusta particularmente "hagámoslo leer como en inglés", pero ese es un asunto diferente.) ¿Cómo expresarías estas pruebas en JUnit? Como 5 pruebas separadas, o 2? (Cada uno de los dos contendría múltiples aserciones, opcionalmente con mensajes). –

+0

En JUnit 4 usaría un corredor personalizado simple, para que pueda usar clases internas como esta: http://github.com/orfjackal/tdd-tetris -tutorial/blob/beyond/src/test/java/tetris/FallingBlocksTest.java En JUnit 3 no funciona tan bien, pero es posible así: http://github.com/orfjackal/tdd-tetris -tutorial/blob/8dcba9521ac8e475566619da0883abafcd8f8b14/src/test/java/tetris/FallingBlocksTest.java En un marco que no tiene fixtures (como lo más indicado), escribiría a regañadientes toda la misma información en el nombre de un método. No tener accesorios produce mucha duplicación. –

4

Si está probando más de una cosa entonces se llama una prueba de integración ... no es una prueba unitaria Todavía ejecutará estas pruebas de integración en el mismo marco de prueba que las pruebas de su unidad.

Las pruebas de integración son generalmente más lentas, las pruebas unitarias son rápidas porque todas las dependencias son falsificadas/falsificadas, por lo que no hay servicio de base de datos/web/llamadas de servicio lento.

Ejecutamos nuestras pruebas unitarias en el compromiso de control de origen, y nuestras pruebas de integración solo se ejecutan en la compilación nocturna.

1

La verdadera pregunta es por qué hacer una prueba o más para todos los métodos, ya que algunas pruebas que abarcan muchos métodos son más simples.

Bueno, de modo que cuando algunas pruebas fallan, usted sabe qué método falla.

Cuando tiene que reparar un automóvil que no funciona, es más fácil saber qué parte del motor está fallando.

Un ejemplo: una clase de lista. ¿Por qué debería hacer pruebas separadas para agregar y eliminar? Una prueba que primero agrega luego elimina los sonidos más simples.

Supongamos que el método de adición está roto y no se agrega, y que el método de eliminación está roto y no se elimina. Su prueba verificará que la lista, después de la adición y eliminación, tenga el mismo tamaño que inicialmente. Tu prueba sería exitosa. Aunque ambos métodos se romperían.

0

Descargo de responsabilidad: Esta es una respuesta muy influenciada por el libro "xUnit Test Patterns".

Prueba sólo una cosa en cada prueba es uno de los principios más básicos que proporciona las siguientes ventajas:

  • Defecto La localización: Si una prueba falla, se puede saber inmediatamente qué ha fallado (idealmente sin más solución de problemas, si has hecho un buen trabajo con las afirmaciones utilizadas).
  • Prueba como una especificación: las pruebas no solo están disponibles como una red de seguridad, sino que también se pueden usar fácilmente como especificaciones/documentación. Por ejemplo, un desarrollador debe ser capaz de leer las pruebas unitarias de un solo componente y comprender la API/contrato de la misma, sin necesidad de leer la implementación (aprovechando el beneficio de la encapsulación).
  • Infectibilidad de TDD: TDD se basa en tener fragmentos de funcionalidad de tamaño pequeño y completar iteraciones progresivas de (escribir prueba de falla, escribir código, verificar la prueba se realiza correctamente). Este proceso se ve muy alterado si una prueba tiene que verificar varias cosas.
  • Falta de efectos secundarios: Algo relacionado con el primero, pero cuando una prueba verifica varias cosas, es más posible que también esté relacionado con otras pruebas. Por lo tanto, es posible que estas pruebas necesiten un accesorio de prueba compartido, lo que significa que uno se verá afectado por el otro. Por lo tanto, con el tiempo es posible que falle una prueba, pero en realidad otra prueba es la que provocó la falla, p. cambiando los datos del dispositivo.

sólo puedo ver una sola razón por la que podría beneficiarse de tener una prueba que verifica varias cosas, pero esto debe ser visto como un olor código realidad:

  • Optimización del rendimiento: Hay algunos casos, donde sus pruebas no se ejecutan solo en la memoria, sino que también dependen del almacenamiento persistente (por ejemplo, bases de datos). En algunos de estos casos, al realizar una prueba, verifique varias cosas que pueden ayudar a disminuir el número de accesos al disco, disminuyendo así el tiempo de ejecución. Sin embargo, las pruebas unitarias deberían ser ejecutables solo en la memoria, por lo que si se encuentra con un caso así, debería reconsiderar si va por el camino equivocado. Todas las dependencias persistentes deben reemplazarse por objetos simulados en pruebas unitarias. La funcionalidad de extremo a extremo debe estar cubierta por un conjunto diferente de pruebas de integración. De esta forma, ya no necesita preocuparse por el tiempo de ejecución, ya que las pruebas de integración generalmente las ejecutan los desarrolladores y no los desarrolladores, por lo que un tiempo de ejecución ligeramente mayor casi no tiene impacto en la eficiencia del ciclo de vida del desarrollo del software.
+0

Una prueba que prueba más de una cosa en la mayoría de los casos tiene menos código que separa las pruebas. Probar dos cosas estrechamente relacionadas se asegura de que las dos cosas realmente funcionen juntas. – iny

+0

Creo que, sin embargo, a lo que se refiere se escapa ligeramente del contexto de las pruebas unitarias y va hacia las pruebas a nivel de componentes. Cuando se realizan pruebas unitarias, lo ideal sería probar cada pieza de funcionalidad completamente aislada. Al realizar pruebas de componentes, es posible que deba probar dos piezas diferentes de funcionalidad juntas, si proporcionan un conjunto mayor de funcionalidades a un nivel superior en la jerarquía de diseño. – Dimos

Cuestiones relacionadas