Soy consciente de que hay muchas preguntas sobre burlas y pruebas, pero no encontré ninguna que me ayudara perfectamente, así que todavía tengo problemas para entender lo siguiente:

Corríjame si me equivoqué, pero por lo que veo, las pruebas unitarias se utilizan para probar la lógica empresarial de una clase en particular de forma aislada y si hay algún objeto necesario desde el exterior, se burlarán de ellas. Entonces, por ejemplo, si tengo un sistema de gestión para ciudadanos de una ciudad simple que agrega ciudadanos a una lista y los devuelve por sus nombres (Suposición: los ciudadanos consisten en solo unos pocos datos personales básicos), como este:

public class ProcessClass {

    ArrayList<Citizen> citizenList = new ArrayList<Citizen>();

    public void addCitizen(Citizen citizen) {
        citizenList.add(citizen);
    }

    public Citizen getByName(String name) {
        for (Citizen c : citizenList) {
            if (c.getName().equals(name)) {
                return c;
            }
        }
        return null;
    }

}

Si ahora quiero probar la unidad de mi ProcessClass, ¿considero que Citizen es una característica externa que debe ser burlada, o simplemente creo un Citizen para fines de prueba? Si se burlan, ¿cómo probaría el método para obtener el objeto por su nombre, ya que el objeto simulado no contiene los parámetros?

5
jle 10 may. 2019 a las 15:54

5 respuestas

La mejor respuesta

Si se burlan, ¿cómo probaría el método para obtener el objeto por su nombre, ya que el objeto simulado no contiene los parámetros?

Puede simular la llamada a getName, utilizando mockito, por ejemplo:

Citizen citizen = mock(Citizen.class);
when(citizen.getName()).thenReturn("Bob");

Aquí hay un ejemplo de una prueba para su método

ProcessClass processClass = new ProcessClass();

Citizen citizen1 = mock(Citizen.class);
Citizen citizen2 = mock(Citizen.class);
Citizen citizen3 = mock(Citizen.class);

@Test
public void getByName_shouldReturnCorrectCitizen_whenPresentInList() {
    when(citizen1.getName()).thenReturn("Bob");
    when(citizen2.getName()).thenReturn("Alice");
    when(citizen3.getName()).thenReturn("John");

    processClass.addCitizen(citizen1);
    processClass.addCitizen(citizen2);
    processClass.addCitizen(citizen3);

    Assert.assertEquals(citizen2, processClass.getByName("Alice"));
}

@Test
public void getByName_shouldReturnNull_whenNotPresentInList() {
    when(citizen1.getName()).thenReturn("Bob");

    processClass.addCitizen(citizen1);

    Assert.assertNull(processClass.getByName("Ben"));
}

Nota:

Yo recomendaría burlarse. Supongamos que escribe 100 pruebas en las que crea una instancia de una clase Citizen de esta manera

Citizen c = new Citizen();

Y unos meses después, su constructor cambia para tomar un argumento, que es un objeto en sí mismo, la clase City por ejemplo. Ahora tiene que regresar y cambiar todas estas pruebas y escribir:

City city = new City("Paris");
Citizen c = new Citizen(city);

Si se burló de Citizen para empezar, no necesitaría hacerlo.

Ahora, como es POJO y su constructor del método getName podría no cambiar, no se debería seguir burlando.

1
Bentaye 10 may. 2019 a las 15:05

A medida que escribe un nuevo código (junto con las nuevas pruebas unitarias) o refactoriza el código existente, desea poder ejecutar las pruebas unitarias una y otra vez para estar razonablemente seguro de que la funcionalidad existente no se rompió. Por lo tanto, las pruebas unitarias deben ser estables y rápidas .

Supongamos que la clase a probar depende de algún recurso externo, como una base de datos. Realiza un cambio de código y las pruebas unitarias fallan repentinamente. ¿Se rompieron las pruebas unitarias debido a un error que acaba de introducir o porque el recurso externo no está disponible? No hay garantía de que el recurso externo siempre esté disponible, por lo que las pruebas unitarias son inestables. Burlarse del recurso externo.

Además, conectarse a un recurso externo puede llevar demasiado tiempo. Cuando finalmente tiene miles de pruebas que se conectan a varios recursos externos, los milisegundos para conectarse al recurso externo se suman, lo que lo ralentiza. Burlarse del recurso externo.

Ahora agregue una canalización CI / CD. Durante la construcción, las pruebas unitarias fallan. ¿El recurso externo está inactivo o su cambio de código rompió algo? ¿Quizás el servidor de compilación no tiene acceso a un recurso externo? Burlarse del recurso externo.

5
Andrew S 10 may. 2019 a las 13:28

La mayoría de las veces, la burla se usa para reemplazar las llamadas reales que son difíciles de duplicar en las pruebas. Por ejemplo, suponga que ProcessClass realiza una llamada REST para recuperar información de Citizen. Para una prueba unitaria simple, sería difícil duplicar esta llamada REST. Sin embargo, puede "burlarse" de RestTemplate y dictar los diferentes tipos de devoluciones para asegurarse de que su código manejaría los 200, 403, etc. Además, podría cambiar el tipo de información para luego probar su código y asegurarse de que se manejan los datos incorrectos. , como información faltante o nula.

En su caso, puede crear un ciudadano y luego probar que el ciudadano es un objeto de la lista o que getByName devuelve el objeto adecuado. Por lo tanto, no es necesario burlarse en este ejemplo.

2
JavaJd 10 may. 2019 a las 13:02

En su ejemplo particular, no, no necesitaría burlarse de nada.

Centrémonos en lo que probarías:

  1. Una prueba donde agrega y recupera una Ciudadana.
  2. agregue 2 ciudadanos, recupere uno
  3. Pase nula en lugar de ciudadana y asegúrese de que su código no se rompa.
  4. agregue dos ciudadanos con el mismo nombre, ¿qué esperaría que suceda entonces?
  5. Añadir una ciudadana sin nombre.
  6. Agregar una ciudadana con un nombre nulo

Etc. etc.

Ya puede ver varias pruebas diferentes que puede escribir.

Para hacerlo más interesante, puede agregar un código a su clase que exponga una versión de solo lectura de su lista de ciudadanos, luego puede verificar que su lista contenga exactamente las cosas correctas.

Por lo tanto, en su escenario no necesita burlarse de nada, ya que no tiene dependencias externas en otro sistema de algún tipo. Ciudadano parece ser una clase de modelo simple, nada más.

2
Andrei Dragotoniu 10 may. 2019 a las 13:43

Para responder la primera parte de tu pregunta

Si ahora quiero probar mi ProcessClass por unidad, ¿considero a Citizen como una característica externa que debe ser burlada, o simplemente creo un Citizen para fines de prueba?

Sin saber más acerca de Citizen es difícil saberlo. Sin embargo, la regla general es que la burla debe hacerse por una razón. Las buenas razones son:

  • No puede hacer que el dependiente del componente (DOC) se comporte fácilmente para sus pruebas.
  • ¿Llamar al DOC causa algún comportamiento no derminístico (fecha / hora, aleatoriedad, conexiones de red)?
  • La configuración de prueba es demasiado compleja y / o requiere mucho mantenimiento (como la necesidad de archivos externos)
  • El DOC original trae problemas de portabilidad para su código de prueba.
  • ¿El uso del DOC original causa tiempos de construcción / ejecución inaceptablemente largos?
  • ¿Los problemas de estabilidad (madurez) del DOC hacen que las pruebas no sean confiables o, peor aún, ¿el DOC aún no está disponible?

Por ejemplo, usted (típicamente) no se burla de las funciones matemáticas de la biblioteca estándar como sin o cos, porque no tienen ninguno de los problemas mencionados anteriormente. En su caso, debe juzgar si solo usar Citizen podría causar alguno de los problemas mencionados anteriormente. Si es así, es muy probable que sea mejor burlarse de él, de lo contrario será mejor que no se burle.

2
Dirk Herrmann 12 may. 2019 a las 18:15