Pruebas unitarias: cómo comenzar

14

Recién estoy comenzando con las pruebas unitarias, pero no estoy seguro si realmente entiendo el punto de todo. Leí tutoriales y libros sobre todo, pero solo tengo dos preguntas rápidas:

  1. Pensé que el propósito de las pruebas unitarias es probar el código que realmente escribimos. Sin embargo, para mí parece que para poder ejecutar la prueba, tenemos que alterar el código original, momento en el que realmente no estamos probando el código que escribimos, sino el código que escribimos para probar.

  2. La mayoría de nuestros códigos se basan en fuentes externas. Sin embargo, al refactorizar nuestro código, incluso si se rompiera el código original, nuestras pruebas seguirían funcionando bien, ya que las fuentes externas son solo incrustaciones dentro de nuestros casos de prueba. ¿No vence el propósito de las pruebas unitarias?

Lo siento si sueno tonto aquí, pero pensé que alguien podría iluminarme un poco.

Gracias por adelantado.

codificador de árboles
fuente

Respuestas:

7

Mi 0.02 $ ... esto es un poco subjetivo, así que tómalo con un grano de sal, pero con suerte te hará pensar y / o generar algún diálogo:

  1. El propósito principal de las pruebas unitarias para mí es garantizar que el código que escribió cumpla con los contratos y casos límite que su código pretende cumplir. Con las pruebas unitarias establecidas, puede asegurarse mejor de que cuando usted (u otra persona en el futuro) refactorice su código, los consumidores externos de su código no se verán afectados si tiene la cobertura estatal adecuada. (al menos en la medida en que pretendes que no se vean afectados).

    En la mayoría de los casos, debería poder escribir código que se pueda enviar a producción y que sea fácilmente comprobable por unidad. Un buen lugar para comenzar puede ser examinar los patrones y marcos de inyección de dependencia. (U otras filosofías para su idioma / plataforma de elección).

  2. Es correcto que las implementaciones externas puedan afectar su código. Sin embargo, garantizar que su código funcione correctamente como parte de un sistema más grande es una función de las pruebas de integración . (Que también podría automatizarse con diferentes grados de esfuerzo).

    Idealmente, su código solo debería basarse en los contratos API de cualquier componente de terceros, lo que significaría que, siempre y cuando sus simulaciones cumplan con la API correcta, sus pruebas unitarias seguirán proporcionando valor.

    Dicho esto, admitiré fácilmente que ha habido ocasiones en las que renuncié a las pruebas unitarias a favor de las pruebas de integración solo, pero solo ha habido casos en los que mi código tuvo que interactuar tan profusamente con componentes de terceros con API mal documentadas. (es decir, la excepción en lugar de la regla).

Charlie
fuente
5
  1. Intenta escribir tus exámenes primero. De esa manera, tendrá una base sólida para el comportamiento de su código y su prueba se convertirá en un contrato para el comportamiento requerido de su código. Por lo tanto, cambiar el código para pasar la prueba se convierte en "cambiar el código para cumplir con el contrato propuesto por la prueba" en lugar de "cambiar el código para pasar la prueba".
  2. Bueno, tenga cuidado con la diferencia entre trozos y simulacros. No verse afectado por ningún cambio en el código es un comportamiento característico de los apéndices, pero no simulacros. Comencemos con la definición del simulacro:

    Un objeto simulado reemplaza un objeto real en condiciones de prueba y permite verificar las llamadas (interacciones) contra sí mismo como parte de una prueba de sistema o unidad.

    -El arte de las pruebas unitarias

Básicamente, sus simulacros deben verificar el comportamiento requerido de sus interacciones. Por lo tanto, si su interacción con la base de datos falla después de una refactorización, su prueba con el simulacro también debería fallar. Por supuesto, esto tiene limitaciones, pero con una planificación cuidadosa, sus simulacros harán mucho más que simplemente "sentarse allí" y no "derrotarán el propósito de las pruebas unitarias".

ruhsuzbaykus
fuente
1

Hacer una buena pregunta no es tonto de ninguna manera.

Abordaré tus preguntas.

  1. El propósito de las pruebas unitarias no es probar el código que ya escribió . No tiene noción de tiempo. Solo en TDD se supone que debes probar primero, pero eso no se aplica estrictamente a ningún tipo de prueba unitaria. El punto es poder probar su programa de manera automática y eficiente en el nivel de clase. Usted hace lo que debe hacer para llegar allí, incluso si eso significa cambiar el código. Y déjame contarte un secreto, a menudo significa eso.
  2. Cuando escribe una prueba, tiene 2 opciones principales para ayudar a garantizar que su prueba sea correcta:

    • Varíe las entradas para cada prueba.
    • Escriba un caso de prueba que garantice que su programa funcione correctamente, y luego escriba un caso de prueba correspondiente que garantice que su programa no funcione como no debería

    Aquí hay un ejemplo:

    TEST(MyTest, TwoPlusTwoIsFour) {
        ASSERT_EQ(4, 2+2);
    }
    
    TEST(MyTest, TwoPlusThreeIsntFour) {
        ASSERT_NE(4, 2+3);
    }
    

    Además de eso, si está probando unitariamente la lógica dentro de su código (no las bibliotecas de terceros), entonces está perfectamente bien que no se preocupe por la ruptura del otro código, en ese contexto. Básicamente, está probando la forma en que su lógica se ajusta y utiliza las utilidades de terceros, que es un escenario de prueba clásico.

Una vez que haya terminado las pruebas a nivel de clase, puede continuar probando la integración entre sus clases (los mediadores, por lo que entiendo) y las bibliotecas de terceros. Estas pruebas se denominan pruebas de integración y no utilizan simulacros, sino implementaciones concretas de todas las clases. Son un poco más lentos, ¡pero siguen siendo muy importantes!

Ñame Marcovic
fuente
1

Parece que tiene una aplicación monolítica que hace todo void main(), desde el acceso a la base de datos hasta la generación de salida. Hay varios pasos aquí antes de que pueda comenzar las pruebas unitarias adecuadas.

1) Encuentra un código que haya sido escrito / copiado más de una vez. Incluso si es justo string fullName = firstName + " " + lastName. Divide eso en un método, como:

private static string GetFullName (firstName, lastName)
{
    return firstName + " " + lastName;
}

Ahora tiene una pieza de código comprobable por unidad, por trivial que sea. Escribe una prueba unitaria para ello. Enjuague y repita este proceso. Eventualmente terminará con una carga de métodos agrupados lógicamente, y podrá extraer varias clases de él. La mayoría de estas clases serán comprobables.

Como beneficio adicional, una vez que haya extraído varias clases, puede extraer interfaces de ellas y actualizar su programa para hablar con las interfaces en lugar de los objetos en sí. En ese momento, puede usar un marco de burla / troquelado (o incluso sus propias falsificaciones hechas a mano) sin cambiar el programa en absoluto. Esto es muy útil una vez que haya extraído las consultas de la base de datos en una clase (o muchas) porque ahora puede falsificar los datos que la consulta debería devolver.

Bryan Boettcher
fuente
0

Algún tipo inteligente dijo: "Si es difícil de probar, probablemente sea un mal código". Es por eso que no es malo reescribir el código, para poder probarlo. El código con dependencias externas es MUY DURO de probar, representa un riesgo para el código y debe evitarse siempre que sea posible, y concentrarse en áreas específicas de integración de su código, fx. Clases tipo fachada / puerta de enlace. Esto hará que un cambio en el componente externo sea mucho más fácil de manejar.

Morten
fuente