- Pruebas de integración de una base de datos usando MariaDB para reemplazar MySql
- Implementación de aplicación multilingüe.
- Guardar archivos en la aplicación y datos sobre ellos en la base de datos.
tipos de pruebas
¿Qué es una prueba? Como dice Wiki: " Una prueba o prueba es una forma de estudiar los procesos subyacentes de un sistema colocando el sistema en diferentes situaciones y rastreando los cambios observables en él". Es decir, se trata de una prueba del correcto funcionamiento de nuestro sistema en determinadas situaciones. Bueno, veamos qué tipos de pruebas hay:-
Las pruebas unitarias son pruebas cuya tarea es probar cada módulo del sistema individualmente. Es deseable que sean piezas del sistema mínimamente divisibles, por ejemplo, módulos.
-
La prueba del sistema es una prueba de alto nivel para probar el funcionamiento de una parte más grande de una aplicación o del sistema en su conjunto.
-
Las pruebas de regresión son pruebas que se utilizan para comprobar si las nuevas funciones o las correcciones de errores afectan la funcionalidad existente de la aplicación y si reaparecen errores antiguos.
-
Las pruebas funcionales consisten en comprobar el cumplimiento de parte de la aplicación con los requisitos establecidos en especificaciones, historias de usuarios, etc.
Tipos de pruebas funcionales:
- prueba de “caja blanca” para comprobar el cumplimiento de parte de la aplicación con los requisitos con conocimiento de la implementación interna del sistema;
- Prueba de “caja negra” para comprobar el cumplimiento de parte de la aplicación con los requisitos sin conocimiento de la implementación interna del sistema.
- Las pruebas de rendimiento son un tipo de pruebas escritas para determinar la velocidad a la que se ejecuta un sistema o parte de él bajo una determinada carga.
- Pruebas de carga : pruebas diseñadas para verificar la estabilidad del sistema bajo cargas estándar y encontrar el pico máximo posible en el que la aplicación funciona correctamente.
- Las pruebas de estrés son un tipo de prueba diseñada para verificar la funcionalidad de una aplicación bajo cargas no estándar y para determinar el pico máximo posible en el que el sistema no fallará.
- Pruebas de seguridad : pruebas utilizadas para comprobar la seguridad de un sistema (de ataques de piratas informáticos, virus, acceso no autorizado a datos confidenciales y otros placeres de la vida).
- Las pruebas de localización son pruebas de localización para una aplicación.
- Las pruebas de usabilidad son un tipo de prueba destinada a comprobar la usabilidad, la comprensibilidad, el atractivo y la capacidad de aprendizaje para los usuarios. Todo esto suena bien, pero ¿cómo funciona en la práctica? Es simple: se utiliza la pirámide de prueba de Mike Cohn: esta es una versión simplificada de la pirámide: ahora está dividida en partes más pequeñas. Pero hoy no pervertiremos y consideraremos la opción más sencilla.
-
Unidad : pruebas unitarias utilizadas en varias capas de la aplicación, que prueban la lógica divisible más pequeña de la aplicación: por ejemplo, una clase, pero generalmente un método. Estas pruebas suelen intentar aislarse lo más posible de la lógica externa, es decir, crear la ilusión de que el resto de la aplicación está funcionando en modo estándar.
Siempre debería haber muchos de estos tests (más que otros tipos), ya que prueban piezas pequeñas y son muy ligeros, no consumen muchos recursos (por recursos me refiero a RAM y tiempo).
-
Integración - pruebas de integración. Verifica partes más grandes del sistema, es decir, si es una combinación de varias partes de lógica (varios métodos o clases) o la exactitud de trabajar con un componente externo. Por lo general, hay menos pruebas de este tipo que pruebas unitarias, ya que son más pesadas.
Como ejemplo de pruebas de integración, puede considerar conectarse a una base de datos y verificar que los métodos que trabajan con ella funcionan correctamente .
-
UI : pruebas que verifican el funcionamiento de la interfaz de usuario. Afectan la lógica en todos los niveles de la aplicación, por eso también se les llama de un extremo a otro. Como regla general, hay muchos menos, ya que son los más pesados y deben revisar los caminos más necesarios (usados).
En la figura anterior vemos la relación de las áreas de diferentes partes del triángulo: aproximadamente la misma proporción se mantiene en el número de estas pruebas en el trabajo real.
Hoy veremos más de cerca las pruebas más utilizadas: las pruebas unitarias, ya que todos los desarrolladores de Java que se precien deberían poder utilizarlas en un nivel básico.
- material sobre cobertura de código en JavaRush y Habré ;
- Teoría fundamental de las pruebas .
- Estamos escribiendo nuestra prueba.
- Ejecutamos la prueba, haya pasado o no (vemos que todo está en rojo, no te asustes: así debe ser).
- Agregamos el código que debería satisfacer esta prueba (ejecutar la prueba).
- Refactorizamos el código.
- Especificación de los datos a probar (accesorios).
- Usando el código bajo prueba (llamando al método bajo prueba).
- Comprobando los resultados y comparándolos con los esperados.
assertEquals(Object expecteds, Object actuals)
— comprueba si los objetos transmitidos son iguales.assertTrue(boolean flag)
— comprueba si el valor pasado devuelve verdadero.assertFalse(boolean flag)
— comprueba si el valor pasado devuelve falso.assertNull(Object object)
– comprueba si el objeto es nulo.assertSame(Object firstObject, Object secondObject)
— comprueba si los valores pasados se refieren al mismo objeto.assertThat(T t, Matcher<T> matcher)
— comprueba si t satisface la condición especificada en el comparador.
Conceptos clave de las pruebas unitarias
La cobertura de la prueba (cobertura del código) es una de las principales evaluaciones de la calidad de las pruebas de la aplicación. Este es el porcentaje de código cubierto por las pruebas (0-100%). En la práctica, mucha gente persigue este porcentaje, con el que no estoy de acuerdo, ya que empiezan a añadir pruebas donde no son necesarias. Por ejemplo, nuestro servicio tiene operaciones CRUD (crear/obtener/actualizar/eliminar) estándar sin lógica adicional. Estos métodos son puramente intermediarios que delegan el trabajo a la capa que trabaja con el repositorio. En esta situación, no tenemos nada que probar: tal vez si este método llama a un método del Tao, pero esto no es serio. Para evaluar la cobertura de las pruebas se suelen utilizar herramientas adicionales: JaCoCo, Cobertura, Clover, Emma, etc. Para un estudio más detallado de este tema, conserve un par de artículos adecuados:Etapas de prueba
La prueba consta de tres etapas:Entornos de prueba
Así que ahora vayamos al grano. Hay varios entornos de prueba (frameworks) disponibles para Java. Los más populares son JUnit y TestNG. Para nuestra revisión, utilizamos: Una prueba JUnit es un método contenido en una clase que se usa sólo para pruebas. Por lo general, una clase recibe el mismo nombre que la clase que está probando con +Test al final. Por ejemplo, CarService→ CarServiceTest. El sistema de compilación Maven incluye automáticamente dichas clases en el área de prueba. De hecho, esta clase se llama clase de prueba. Repasemos un poco las anotaciones básicas: @Test : definición de este método como método de prueba (de hecho, el método marcado con esta anotación es una prueba unitaria). @Before : marca el método que se ejecutará antes de cada prueba. Por ejemplo, completar datos de prueba de clase, leer datos de entrada, etc. @After : se coloca encima del método que se llamará después de cada prueba (limpiar datos, restaurar valores predeterminados). @BeforeClass - colocado encima del método - análogo a @Before. Pero este método se llama sólo una vez antes de todas las pruebas para una clase determinada y, por lo tanto, debe ser estático. Se utiliza para realizar operaciones más pesadas, como levantar una base de datos de prueba. @AfterClass es lo opuesto a @BeforeClass: se ejecuta una vez para una clase determinada, pero se ejecuta después de todas las pruebas. Se utiliza, por ejemplo, para limpiar recursos persistentes o desconectarse de la base de datos. @Ignore : señala que el método siguiente está deshabilitado y se ignorará al ejecutar pruebas en general. Se utiliza en diferentes casos, por ejemplo, si se cambió el método base y no hubo tiempo para rehacer la prueba. En tales casos, también es recomendable agregar una descripción: @Ignore("Alguna descripción"). @Test (esperado = Exception.class): se utiliza para pruebas negativas. Estas son pruebas que verifican cómo se comporta un método en caso de error, es decir, la prueba espera que el método arroje alguna excepción. Dicho método se indica mediante la anotación @Test, pero con un error que detectar. @Test(timeout=100) : comprueba que el método se ejecute en no más de 100 milisegundos. @Mock : se usa una clase sobre un campo para establecer un objeto determinado como simulado (esto no es de la biblioteca Junit, sino de Mockito), y si lo necesitamos, estableceremos el comportamiento del simulacro en una situación específica. , directamente en el método de prueba. @RunWith(MockitoJUnitRunner.class) : el método se coloca encima de la clase. Este es el botón para ejecutar pruebas en él. Los corredores pueden ser diferentes: por ejemplo, existen los siguientes: MockitoJUnitRunner, JUnitPlatform, SpringRunner, etc.). En JUnit 5, la anotación @RunWith fue reemplazada por la anotación @ExtendWith más poderosa. Echemos un vistazo a algunos métodos para comparar resultados:assertThat(firstObject).isEqualTo(secondObject)
aquí hablé sobre los métodos básicos, ya que el resto son variaciones diferentes de los anteriores.
Práctica de prueba
Ahora veamos el material anterior usando un ejemplo específico. Probaremos el método del servicio: actualización. No consideraremos la capa dao, ya que es nuestra predeterminada. Agreguemos un iniciador para las pruebas:<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
Entonces, la clase de servicio:
@Service
@RequiredArgsConstructor
public class RobotServiceImpl implements RobotService {
private final RobotDAO robotDAO;
@Override
public Robot update(Long id, Robot robot) {
Robot found = robotDAO.findById(id);
return robotDAO.update(Robot.builder()
.id(id)
.name(robot.getName() != null ? robot.getName() : found.getName())
.cpu(robot.getCpu() != null ? robot.getCpu() : found.getCpu())
.producer(robot.getProducer() != null ? robot.getProducer() : found.getProducer())
.build());
}
}
8 - extraiga el objeto actualizado de la base de datos 9-14 - cree el objeto a través del constructor, si el objeto entrante tiene un campo - configúrelo, si no - deje lo que está en la base de datos Y mire nuestra prueba:
@RunWith(MockitoJUnitRunner.class)
public class RobotServiceImplTest {
@Mock
private RobotDAO robotDAO;
private RobotServiceImpl robotService;
private static Robot testRobot;
@BeforeClass
public static void prepareTestData() {
testRobot = Robot
.builder()
.id(123L)
.name("testRobotMolly")
.cpu("Intel Core i7-9700K")
.producer("China")
.build();
}
@Before
public void init() {
robotService = new RobotServiceImpl(robotDAO);
}
1 - nuestro Runner 4 - aísla el servicio de la capa dao sustituyendo un simulacro 11 - establece una entidad de prueba para la clase (la que usaremos como hámster de prueba) 22 - establece un objeto de servicio que probaremos
@Test
public void updateTest() {
when(robotDAO.findById(any(Long.class))).thenReturn(testRobot);
when(robotDAO.update(any(Robot.class))).then(returnsFirstArg());
Robot robotForUpdate = Robot
.builder()
.name("Vally")
.cpu("AMD Ryzen 7 2700X")
.build();
Robot resultRobot = robotService.update(123L, robotForUpdate);
assertNotNull(resultRobot);
assertSame(resultRobot.getId(),testRobot.getId());
assertThat(resultRobot.getName()).isEqualTo(robotForUpdate.getName());
assertTrue(resultRobot.getCpu().equals(robotForUpdate.getCpu()));
assertEquals(resultRobot.getProducer(),testRobot.getProducer());
}
Aquí vemos una clara división de la prueba en tres partes: 3-9 - configuración de accesorios 11 - ejecución de la parte probada 13-17 - verificación de los resultados Más detalles: 3-4 - configuración del comportamiento de moka dao 5 - configuración de una instancia que actualizaremos además de nuestro estándar 11 - use el método y tome la instancia resultante 13 - verifique que no sea cero 14 - verifique el ID del resultado y los argumentos del método especificado 15 - verifique si el nombre se ha actualizado 16 - mire en el resultado de la CPU 17: dado que no configuramos esto en el campo de instancia de actualización, debería permanecer igual, verifiquémoslo. Comencemos: la prueba está en verde, puede exhalar)) Entonces, resumamos: las pruebas mejoran la calidad del código y hacen que el proceso de desarrollo sea más flexible y confiable. Imagínese cuánto esfuerzo tendríamos que dedicar al rediseñar un software con cientos de archivos de clase. Una vez que tengamos pruebas unitarias escritas para todas estas clases, podremos refactorizar con confianza. Y lo más importante, nos ayuda a encontrar errores fácilmente durante el desarrollo. Chicos, eso es todo para mí hoy: agreguen me gusta, escriban comentarios)))
GO TO FULL VERSION