Documentos de Académico
Documentos de Profesional
Documentos de Cultura
PRUEBAS DE SOFTWARE 2
Índice
Presentación 5
Red de contenidos 6
2.2 Tema 4 : Pruebas Integrales con lenguaje Gherkin integrado con Cucumber, 87
Serenity y Rest Assured
2.2.1 : Creación y Ejecución de una prueba integral simple con Gherkin, 87
Cucumber, Serenity y Rest Assured en IntelliJ
2.2.2 : Creación y Ejecución de una prueba integral de regular complejidad 96
con Gherkin, Cucumber, Serenity y Rest Assured en IntelliJ
2.2.3 : Creación y Ejecución de una prueba integral avanzada con Gherkin, 107
Cucumber, Serenity y Rest Assured en IntelliJ
3.2 Tema 6 : Tema 6: Pruebas Funcionales web utilizando Selenium y lenguaje 135
Gherkin integrado con Cucumber y Serenity
4.2 Tema 8 : Pruebas Funcionales para móviles utilizando Appium y lenguaje 212
Gherkin integrado con Cucumber y Serenity
4.2.1 : Creación y Ejecución de una prueba funcional móvil simple 212
utilizando Appium y lenguaje Gherkin integrado con Cucumber y
Serenity
4.2.2 : Creación y Ejecución de una prueba funcional móvil de regular 216
complejidad utilizando Appium y lenguaje Gherkin integrado con
Cucumber y Serenity
4.2.3 : Creación y Ejecución de una prueba funcional móvil avanzada 219
utilizando Appium y lenguaje Gherkin integrado con Cucumber y
Serenity
Bibliografía 260
Presentación
Pruebas de Software es un curso que pertenece a la línea de carrera y se dicta en la carrera
profesional de Computación e Informática. Brinda los conceptos básicos relacionados al área de
control de la calidad de software y administración de pruebas de software, alineados a las
mejores prácticas en desarrollo de software.
Red de contenidos
Pruebas de Software
UNIDAD
1
FUNDAMENTOS DE TDD
LOGRO DE LA UNIDAD DE APRENDIZAJE
Al término de la unidad, el alumno reconoce la importancia de las Pruebas de Software
para el control de calidad del producto de software. Asimismo, asimila los conceptos y
aplica Test Driven Development (TDD) en una aplicación Java utilizando JUnit y Mockito.
TEMARIO
1.1 Tema 1 : Introducción a TDD
1.1.1 : Pruebas Unitarias
1.1.2 : TDD
1.1.3 : Pruebas de Integración
1.1.4 : Pruebas Funcionales
1.1.5 : Pruebas de Sistema
1.1.6 : Pruebas de Aceptación
ACTIVIDADES PROPUESTAS
Las pruebas se consideran parte de un proceso más amplio de verificación y validación (V&V)
del software. Aunque ambas no son lo mismo, se confunden con frecuencia. Barry Boehm,
pionero de la ingeniería de software, expresó de manera breve la diferencia entre las dos
(Boehm, 1979):
Los procesos de verificación y validación buscan comprobar que el software por desarrollar
cumpla con sus especificaciones, y brinde la funcionalidad deseada por las personas.
1) Propósito del software: Cuanto más crítico sea el software, más importante debe
ser su confiabilidad. Por ejemplo, el nivel de confianza requerido para que se use el
software en el control de un sistema crítico de seguridad es mucho mayor que el
requerido para un prototipo desarrollado para demostrar nuevas ideas del
producto.
2) Expectativas del usuario: Debido a su experiencia con software no confiable y
plagado de errores, muchos usuarios tienen pocas expectativas de la calidad del
software, por lo que no se sorprenden cuando éste falla. Al instalar un sistema, los
usuarios podrían soportar fallas, porque los beneficios del uso exceden los costos
de la recuperación de fallas. Ante tales situaciones, no es necesario dedicar mucho
tiempo en la puesta a prueba del software. Sin embargo, conforme el software se
completa, los usuarios esperan que se torne más confiable, de modo que pueden
requerirse pruebas exhaustivas en versiones posteriores.
3) Entorno de mercado: Cuando un sistema se comercializa, los vendedores del
sistema deben considerar los productos competitivos, el precio que los clientes
están dispuestos a pagar por un sistema y la fecha requerida para entregar dicho
sistema. En un ambiente competitivo, una compañía de software puede decidir
lanzar al mercado un programa antes de estar plenamente probado y depurado,
pues quiere que sus productos sean los primeros en ubicarse. Si un producto de
software es muy económico, los usuarios tal vez toleren un nivel menor de
fiabilidad.
La figura 1.1 presenta un modelo abstracto del proceso de prueba “tradicional”, como
se utiliza en el desarrollo dirigido por un plan. Los casos de prueba son especificaciones
de las entradas a la prueba y la salida esperada del sistema (los resultados de la prueba),
además de información sobre lo que se pone a prueba. Los datos de prueba son las
entradas que se diseñaron para probar un sistema. En ocasiones pueden generarse
automáticamente datos de prueba; no obstante, es imposible la generación automática
de casos de prueba, pues debe estar implicada gente que entienda lo que se supone que
tiene que hacer el sistema para especificar los resultados de prueba previstos. Sin
embargo, es posible automatizar la ejecución de pruebas. Los resultados previstos se
comparan automáticamente con los resultados establecidos, de manera que no haya
necesidad de que un individuo busque errores y anomalías al correr las pruebas.
(Sommerville, 2011, p.206)
Adaptado de Sommerville I., 2011. Ingeniería de Software (9.ª ed.). Editorial Addison-Wesley.
El Shift Left Testing es un enfoque para la prueba de software en el que la prueba se realiza antes
en el ciclo de vida: “Comience las actividades de prueba lo antes posible en el ciclo de vida del
desarrollo. Pruebe temprano y con frecuencia” (figura 1.4).
Dado que es más fácil y económico corregir los errores detectados anteriormente en el proceso,
se introdujeron nuevas estrategias de cambiar las pruebas antes en el ciclo de vida de desarrollo
de software para ayudar a identificar los problemas lo antes posible. La detección temprana
acelera el proceso de tomar medidas correctivas, lo que reduce el tiempo y el costo de
solucionar estos problemas. Shift-Left básicamente significa comenzar a probar lo antes posible
(desplazarse a la izquierda en el eje del ciclo de vida).
Shift-Left se trata de descubrir tantos problemas como sea posible lo antes posible en el proceso
de desarrollo de software, por lo que el costo de solucionarlos está bajo control y también
contribuye a mejorar la calidad de los productos enviados (figura 1.5).
Otro factor extremadamente importante para el éxito del enfoque Shift-Left es establecer una
cultura de calidad que sea responsabilidad de todos, incluidos los desarrolladores, los ingenieros
de operaciones de desarrollo, los analistas comerciales, el soporte de producción, las partes
interesadas comerciales e incluso los gerentes.
El siguiente cuadro muestra una muestra de actividades por fase, que el equipo podría llevar a
cabo como una implementación de un enfoque Shift-Left (figura 1.6):
Beneficios de Shift-Left:
Shift-Left hace más que ayudar a los equipos de TI a encontrar defectos antes. También puede
ayudar a un equipo a colaborar mejor con las partes interesadas, mejorar la competencia
colectiva y crear escenarios de prueba más realistas; en general, brinda varios beneficios
culturales.
Cambiar de mentalidad: defina quality gates y prácticas de calidad a lo largo del ciclo de
desarrollo.
Cambiar de responsabilidades: la calidad es responsabilidad de todos.
Cambiar en el tiempo: pruebe temprano.
Cambiar en la organización: menos evaluadores, más QE (ingenieros de calidad). Diseñe
escenarios de prueba integrales en lugar de casos de prueba aislados.
Adaptado de Rodríguez, C. (2019, Diciembre 19). Automatizar Pruebas de Software: Consideraciones y herramientas. Abstracta.
https://cl.abstracta.us/blog/automatizar-pruebas-de-software/
Las Pruebas Unitarias (o Unit Testing), representan el alma de la programación dirigida por
pruebas. Son test que se encargan de verificar -de manera simple y rápida- el comportamiento
de una parte mínima de código, de forma independiente y sin alterar el funcionamiento de otras
partes de la aplicación.
Es una prueba para asegurarnos que la porción de código o funcionalidad hace lo que debería
hacer, independientemente del resto de la aplicación.
El objetivo de las pruebas unitarias es probar el comportamiento de cada uno de los métodos
de la clase de manera aislada.
Una prueba unitaria posee cuatro características particulares que debe guardar para
considerarse “unitario”. Estas características son:
4) Rápido: Un sólo test tendría que ejecutarse en una pequeña fracción de segundo.
La velocidad de ejecución de una prueba unitaria cumple un papel fundamental e
ineludible en el desarrollo guiado por pruebas, ya que, de la velocidad de ejecución de
un test, dependerá de manera proporcional, la velocidad con la que una funcionalidad
se desarrolle.
Los desarrolladores utilizamos los tests unitarios para asegurarnos de que el código funciona
como esperamos que funcione, al igual que el cliente usa los tests de cliente para asegurarse
que los requisitos de negocio se alcancen como se espera que lo hagan.
Por lo tanto, los test unitarios se agrupan en clases, denominadas Test, que heredan de una clase
del framework xUnit.
Un test tiene tres partes, que se identifican con las siglas AAA en inglés: Arrange (Preparar), Act
(Actuar), Assert (Afirmar). Una parte de la preparación puede estar contenida en el método
SetUp, si es común a todos los tests de la clase. Si la etapa de preparación es común a varios
tests de la clase, pero no a todos, entonces podemos definir otro método o función en la misma
clase, que aúne tal código. No le pondremos la etiqueta de test, sino que lo invocaremos desde
cada punto en que lo necesitemos.
Los métodos contenidos en una clase Test Case, pueden o no, ser Test Unitarios. Los Tests
Unitarios contenidos en una clase Test Case, deben contener la anotación @test en el método
a fin de que el framework los identifique como tales.
JUnit es un framework, creado por Erich Gamma y Kent Beck, utilizado para automatizar las
pruebas unitarias en el lenguaje Java. Las pruebas en JUnit se realizan por medio de casos de
prueba escritos en clases Java.
Una de las grandes utilidades de JUnit es que los casos de prueba quedan definidos para ser
ejecutados en cualquier momento, por lo cual es sencillo, luego de realizar modificaciones al
programa, comprobar que los cambios no introdujeron nuevos errores. Esto último es conocido
como pruebas de regresión. Cabe destacar que para que un método de prueba sea exitoso o no,
son usados los métodos “assert”, tanto para comparar los valores esperados contra los valores
reales, o directamente hacer que el método de prueba falle. En la tabla 1.1 se describen los
métodos implementados de JUnit.
comprueba que el
assertSame(objeto_esperado, objeto_real) objeto_esperado y el objeto_real sean
el mismo objeto
Se procede con la instalación del IntelliJ IDEA edición Community. Luego, generamos un nuevo
proyecto Java con su respectiva clase llamada “Suma”:
package pe.edu.cibertec;
Generar clase SumaTest, para ello seleccionar la clase Suma, clic derecho opción “Generate”,
opción “Test”, colocar Class Name “SumaTest”:
Dentro de la clase SumaTest, posicionarse sobre el error en la palabra junit (marcado con rojo)
y seleccionar la opción “More Options”; luego seleccionar la opción “Add 'JUnit5.8.1' to
classpath”:
El error de JUnit desaparecerá, sin embargo, es necesario recargar Maven, observar que se carga
una imagen con la letra “m”(Maven), hacer clic para recargar los cambios en Maven:
package pe.edu.cibertec;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class SumaTest {
@Test
public void sumaPositivos() {
System.out.println("Sumando dos números positivos ...");
Suma S = new Suma(2, 3);
assertTrue(S.sumar() == 5);
}
@Test
public void sumaNegativos() {
System.out.println("Sumando dos números negativos ...");
Suma S = new Suma(-2, -3);
assertTrue(S.sumar() == -5);
}
@Test
public void sumaPositivoNegativo() {
System.out.println("Sumando un número positivo y un
número negativo ...");
Suma S = new Suma(2, -3);
assertTrue(S.sumar() == -1);
}
}
1.1.2. TDD
El Desarrollo Dirigido por Tests (Test Driven Development), es una técnica de diseño e
implementación de software incluida dentro de la metodología XP (eXtreme Programming). TDD
es una técnica para diseñar software que se centra en tres pilares fundamentales:
JUnit es una herramienta para trabajar aplicando una metodología conocida como Test Driven
Development (TDD) o Desarrollo Guiado por Pruebas. TDD es un estilo para plantear el
desarrollo de un software, que se basa en el hecho de que el programador vuelca su
conocimiento sobre determinado problema en forma de aserciones. El programador debe
codificar lo necesario para validar estas aserciones que enunció anteriormente. Cuando todas
sean válidas debe seguir explorando el dominio del problema agregando más aserciones.
Cuando alguna resulte falsa, debe codificar para hacerla verdadera. Finalmente, el programador
refactoriza el código y lo repara buscando abstracciones, de forma de simplificarlo, pero
procurando que el comportamiento no cambie; las aserciones deben continuar siendo válidas.
TDD ofrece una forma de abordar el problema de manera exploratoria, usando ejemplos
(aserciones). Esto es más fácil que tratar de resolver el problema en abstracto y para
todos los casos. Así mismo TDD hace que el programador sea el usuario de su propio
código primero, logrando que él mismo pueda darse cuenta de cuán amigable y fácil de
usar es el API desarrollado, también facilita el mantenimiento del software. El software,
por su naturaleza, está en constante cambio. Estos cambios tienen que ser codificados e
introducidos en el código existente sin afectar involuntariamente al resto de las
funcionalidades del sistema. Así, los tests sirven como red de seguridad, dado que
podremos verificar rápida y sistemáticamente el correcto funcionamiento de la
aplicación.
En JUnit se definen casos de prueba, representados por una clase que contiene métodos
de prueba. Cada uno de estos métodos tiene el objetivo de probar uno o algunos
ejemplos y aserciones. JUnit luego ejecuta los casos y genera un reporte que informa
cuáles pasaron exitosamente y cuáles fallaron o dieron error. (Vivona, 2011, p.37)
1) Escribir un test: Debe ser el más sencillo que se nos ocurra, debe fallar al correrlo
2) Correr todos los tests: Si hay errores, debemos implementar lo mínimo necesario para
que pasen y volver al paso 2
3) Reflexiono - ¿Se puede mejorar el código?
Sí: entonces Refactorizar y volver al paso 2
No: Volver al paso 1
Tienen como objetivo verificar la correcta integración de todas las unidades probadas en la
prueba unitaria (integración interna) y la correcta integración con otros sistemas involucrados
(integración externa). Normalmente se realizan tras las pruebas unitarias.
Los tests de integración se pueden ver como tests de sistema pequeños, también se escriben
utilizando herramientas xUnit y tienen un aspecto parecido a los tests unitarios. Como su
nombre indica, integración significa que ayuda a unir distintas partes del sistema. Es el
complemento a los tests unitarios. Un test de integración podría ser aquel que ejecuta la capa
de negocio y después consulta la base de datos para afirmar que todo el proceso, desde negocio
hacia abajo, fue correcto. El número de tests de integración tiende a ser menor que el número
de tests unitarios. Una vez que se ha probado que dos módulos, objetos o capas se integran
bien, no es necesario repetir el test para otra variante de la lógica de negocio; para eso habrá
varios tests unitarios.
Una prueba funcional es una prueba de tipo caja negra basada en la ejecución, revisión
y retroalimentación de las funcionalidades previamente diseñadas para el software. Las
pruebas funcionales se hacen mediante el diseño de modelos de prueba que buscan
evaluar cada una de las opciones con las que cuenta el paquete informático. Dicho de
otro modo, son pruebas específicas, concretas y exhaustivas para probar y validar que el
software hace lo que debe y, sobre todo, lo que se ha especificado (Calikli, Uzundağ y
Bener, 2010).
Ya que las pruebas funcionales son de tipo caja negra, estas pueden ser ejecutadas sin
tener conocimiento de los mecanismos internos del software bajo análisis. Esto significa
que los probadores de software (testers) no necesitan saber lenguajes de programación
o cómo se ha implementado el software (Calikli, Uzundağ y Bener, 2010).
Su objetivo es verificar el comportamiento del sistema en su totalidad. Para ello, las pruebas de
Sistema se afrontan desde una doble perspectiva para cubrir tanto los requisitos Funcionales
como los No Funcionales del sistema bajo prueba. Normalmente se realizan tras las pruebas de
integración.
Es el mayor de los tests de integración, ya que integra varias partes del sistema. Se trata de un
test que puede ir, incluso, de extremo a extremo de la aplicación o del sistema. Se habla de
sistema porque es un término más general que aplicación, pero no se refiere a administración
de sistemas, no es que estemos probando el servidor web o el servidor SMTP, aunque, tales
servicios, podrían ser una parte de nuestro sistema. Así ́ pues, un test del sistema se ejercita tal
cual lo haría el usuario humano, usando los mismos puntos de entrada (aquí ́ sí es la interfaz
gráfica) y llegando a modificar la base de datos o lo que haya en el otro extremo. ¿Cómo se
puede automatizar el uso de la interfaz de usuario y validar que funciona? Hay software que
permite hacerlo. Por ejemplo, si la interfaz de usuario es web, el plugin Selenium para el
navegador Mozilla Firefox nos permite registrar nuestra actividad en una página web como si
estuviéramos grabando un vídeo para luego reproducir la secuencia automáticamente y
detectar cambios en la respuesta del sitio web. Pongamos que grabo la forma en que relleno un
formulario con una dirección de correo electrónico incorrecta para que el sitio web me envíe un
mensaje de error. Cada vez que quiera volver a comprobar que el sitio web responde igual ante
esa entrada, sólo tengo que ejecutar el test generado por Selenium. Hay herramientas que
permiten hacer lo mismo mediante programación: nos dan una API para seleccionar controles
gráficos, y accionarlos desde código fuente, comprobando el estado de la ejecución con
sentencias condicionales o asertivas. El propio Selenium lo permite.
Los tests de sistema son muy frágiles en el sentido de que cualquier cambio en cualquiera de las
partes que componen el sistema, puede romperlos. No es recomendable escribir un gran
número de ellos por su fragilidad. Si la cobertura de otros tipos de tests de granularidad más
fina, como por ejemplo los unitarios, es amplia, la probabilidad de que los errores sólo se
detecten con tests de sistema es muy baja. Es decir, que, si hemos ido haciendo TDD, no es
productivo escribir tests de sistema para todas las posibles formas de uso del sistema, ya que
esta redundancia se traduce en un aumento del costo de mantenimiento de los tests. Por el
contrario, si no tenemos escrito absolutamente ningún tipo de test, blindar la aplicación con
tests de sistema será́ el paso más recomendable antes de hacer modificaciones en el código
fuente. Luego, cuando ya hubiesen tests unitarios para los nuevos cambios introducidos, se
podrían ir desechando tests de sistema.
Están orientadas a la aceptación final del producto por parte del usuario. El objetivo principal de
este nivel de pruebas es dar confianza al usuario de que el sistema funcionará de acuerdo con
sus expectativas, cumpliendo los requisitos funcionales y operativos esperados. Normalmente
se realizan tras las pruebas de sistema.
¿Los tests de aceptación no usan la interfaz de usuario del programa? Podría ser que sí, pero en
la mayoría de los casos la respuesta debe ser no. Los tests de carga y de rendimiento son de
aceptación cuando el cliente los considera requisitos de negocio.
Resumen
Los beneficios de la automatización de pruebas son los siguientes:
Recursos
Pueden revisar los siguientes enlaces para ampliar los conceptos vistos en esta unidad:
o https://www.youtube.com/watch?v=74sClDEYSQ4
o https://www.youtube.com/watch?v=1l85cSHqCIE&list=PLsRPgBUE5BI68-
h4MF0Y5FLSNwGdlIBK0
o https://www.youtube.com/watch?v=IHDQNKE1tUA
package pe.edu.cibertec.Tema2Sesion1;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.math.BigDecimal;
import java.math.RoundingMode;
@SpringBootTest
class Tema2Sesion1ApplicationTest {
@Test
void contextLoads() {
}
@Test
public void ejemploAssertEquals1() {
assertEquals(1, 2);
}
@Test
public void ejemploAssertEquals2() {
assertEquals(2, 2);
}
@Test
public void ejemploAssertEquals3() {
assertEquals("2", 2);
}
@Test
public void ejemploAssertEquals4() {
assertEquals("2", "2");
}
@Test
public void ejemploAssertEquals5() {
assertEquals(2, 2.0);
}
@Test
public void ejemploAssertEquals6() {
assertEquals(2.0, 2.0);
}
@Test
public void ejemploAssertEquals7() {
double a = 2.0;
double b = 2.0;
assertEquals(a, b);
}
@Test
public void ejemploAssertEquals8() {
BigDecimal a = BigDecimal.valueOf(2.0).setScale(1,
RoundingMode.UP);
BigDecimal b = BigDecimal.valueOf(2.0).setScale(1,
RoundingMode.UP);
assertEquals(a, b);
}
@Test
public void ejemploAssertEquals9() {
assertEquals('2', 2);
}
@Test
public void ejemploAssertEquals10() {
String a = "hola";
String b = "hol"+"a";
String c = b;
assertEquals(a, c);
}
@Test
public void ejemploAssertTrue1() {
assertTrue(1 == 1);
}
@Test
public void ejemploAssertTrue2() {
assertTrue(1 == 2);
}
@Test
public void ejemploAssertTrue3() {
assertTrue(1 != 2);
}
@Test
public void ejemploAssertTrue4() {
assertTrue(2.0 == 2.0);
}
@Test
public void ejemploAssertTrue5() {
assertTrue(2.1 == 2.10);
}
@Test
public void ejemploAssertFalse1() {
assertFalse(1 == 1);
}
@Test
public void ejemploAssertFalse2() {
assertFalse(1 == 2);
}
@Test
public void ejemploAssertFalse3() {
assertFalse(1 != 2);
}
@Test
public void ejemploAssertFalse4() {
assertFalse(2.0 != 2.0);
}
@Test
public void ejemploAssertFalse5() {
assertFalse(2.1 != 2.10);
}
@Test
public void ejemploAssertNull1() {
String nombre=null;
assertNull(nombre);
}
@Test
public void ejemploAssertNull2() {
String nombre="a";
assertNull(nombre);
@Test
void ejemploAssertNotNull1() {
String nombre=null;
assertNotNull(nombre);
}
@Test
public void ejemploAssertNotNull2() {
String nombre="a";
assertNotNull(nombre);
}
@Test
public void ejemploAssertSame1() {
assertSame("Hola","Hola");
}
@Test
public void ejemploAssertSame2() {
assertSame("Hola","HolA");
}
@Test
public void ejemploAssertSame3() {
String nombre1="Hola";
String nombre2="Hola";
assertSame(nombre1, nombre2);
}
@Test
public void ejemploAssertSame4() {
assertSame('2', 2);
}
@Test
public void ejemploAssertSame5() {
assertSame("2", 2);
}
@Test
public void ejemploAssertSame6() {
assertSame(2, 2);
}
@Test
public void ejemploAssertSame7() {
String a = "hola";
String b = "hol"+"a";
String c = b;
assertSame(a, c);
}
@Test
public void ejemploAssertNotSame1() {
assertNotSame("Hola","Hola");
}
@Test
@Test
public void ejemploAssertNotSame3() {
String nombre1="Hola";
String nombre2="Hola";
assertNotSame(nombre1, nombre2);
}
@Test
public void ejemploAssertArrayEquals1() {
String[] nombres1 = { "java", "junit", "jboss" };
String[] nombres2 = { "java", "junit", "jboss" };
assertArrayEquals(nombres1, nombres2);
}
@Test
public void ejemploAssertArrayEquals2() {
String[] nombres1 = { "java", "junit", "jboss" };
String[] nombres2 = { "java", "junit", "jbosS" };
assertArrayEquals(nombres1, nombres2);
}
}
Ejecutar las pruebas unitarias y analizar con su profesor los resultados obtenidos:
Figura 1.31: Ejecutar pruebas unitarias
Ahora comencemos a aplicar TDD y para ello enumeraremos los pasos para aplicarlo:
1) Escribir un test: Debe ser el más sencillo que se nos ocurra, debe fallar al correrlo.
2) Correr todos los tests: Si hay errores, debemos implementar lo mínimo necesario para
que pasen y volver al paso 2.
3) Reflexiono - ¿Se puede mejorar el código?
Sí: entonces Refactorizar y volver al paso 2
No: Volver al paso 1
Veamos el ejemplo para determinar si un año es bisiesto bajo las siguientes condiciones:
Un año es bisiesto si es divisible por cuatro lo que provoca que uno de cada cuatro años
sea bisiesto.
Para un mayor ajuste los años divisibles por 100 no serán bisiestos, de tal forma que
cada 100 años habrá un año que debería ser bisiesto y no lo es.
Sin embargo, si el año es divisible por 400 sí que es bisiesto, así cada 400 años habrá un
año que no debería ser bisiesto, pero sí que lo es.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
}
}
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
Finalmente podemos agregar otros tests que deben pasar sin problemas y nuestro código Java
de tests quedaría como sigue:
package pe.edu.cibertec;
import org.junit.jupiter.api.Test;
Veamos otro ejemplo en que debemos determinar si una contraseña ingresada al sistema
cumple los lineamientos de PCI (normas de seguridad de la industria de Tarjetas de Crédito) que
dice que debe tener una longitud mínima de siete caracteres y debe ser una combinación de
caracteres numéricos y alfabéticos.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
}
}
import java.util.regex.Pattern;
import java.util.regex.Pattern;
Finalmente podemos agregar otros tests que deben pasar sin problemas y nuestro código Java
de tests quedaría como sigue:
package pe.edu.cibertec;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
Aplicando la técnica Test Driven Development elaborar un programa que nos permita
determinar el tipo de triángulo que corresponde a la longitud de los lados ingresados, según las
siguientes reglas:
1) Elabore los tests necesarios que le permita construir el programa solicitado aplicando la
técnica TDD.
2) Elabore el programa considerando los pasos de la técnica TDD.
3) Optimice el código aplicando Refactorización.
import org.junit.jupiter.api.Test;
@Test
public void trianguloNoExisteFalse() {
System.out.println("Triangulo No Existe: 0, 20, 20");
Triangulo c = new Triangulo(0, 20, 20);
assertFalse(c.validarTipoTriangulo() != 0);
}
@Test
public void trianguloNoExisteEquals() {
System.out.println("Triangulo No Existe: 0, 20, 20");
Triangulo c = new Triangulo(0, 20, 20);
assertEquals(c.validarTipoTriangulo(), 0);
}
@Test
@Test
public void trianguloEquilateroFalse() {
System.out.println("Triangulo Equilatero: 20, 20, 20");
Triangulo c = new Triangulo(20, 20, 20);
assertFalse(c.validarTipoTriangulo() != 1);
}
@Test
public void trianguloEquilateroEquals() {
System.out.println("Triangulo Equilatero: 20, 20, 20");
Triangulo c = new Triangulo(20, 20, 20);
assertEquals(c.validarTipoTriangulo(), 1);
}
@Test
public void trianguloIsocelesTrue() {
System.out.println("Triangulo Isoceles: 20, 20, 10");
Triangulo c = new Triangulo(20, 20, 10);
assertTrue(c.validarTipoTriangulo() == 2);
}
@Test
public void trianguloIsocelesFalse() {
System.out.println("Triangulo Isoceles: 20, 20, 10");
Triangulo c = new Triangulo(20, 20, 10);
assertFalse(c.validarTipoTriangulo() != 2);
}
@Test
public void trianguloIsocelesEquals() {
System.out.println("Triangulo Isoceles: 20, 20, 10");
Triangulo c = new Triangulo(20, 20, 10);
assertEquals(c.validarTipoTriangulo(), 2);
}
@Test
public void trianguloEscalenosTrue() {
System.out.println("Triangulo Escaleno: 10, 20, 25");
Triangulo c = new Triangulo(10, 20, 25);
assertTrue(c.validarTipoTriangulo() == 3);
}
@Test
public void trianguloEscalenosFalse() {
System.out.println("Triangulo Escaleno: 10, 20, 25");
Triangulo c = new Triangulo(10, 20, 25);
assertFalse(c.validarTipoTriangulo() != 3);
}
@Test
public void trianguloEscalenosEquals() {
System.out.println("Triangulo Escaleno: 10, 20, 25");
Triangulo c = new Triangulo(10, 20, 25);
assertEquals(c.validarTipoTriangulo(), 3);
}
}
Figura 1.35: Resultado ejecución TrianguloTest
Utilizando la técnica Test Driven Development desarrolle un programa que calcule el Impuesto
a la Renta a pagar por una Persona Natural dado que se tiene su ingreso anual. Según este
ingreso anual se tiene una escala de tasas de impuesto que debe pagar la cual debe ser calculada
escalonadamente (por cada tramo):
import org.junit.jupiter.api.Test;
@Test
public void tasa0False() {
ImpuestoRenta c = new ImpuestoRenta(4000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 0%: " + iR);
assertFalse(c.calcularImpuesto() != 0.00);
}
@Test
@Test
public void tasa8True() {
ImpuestoRenta c = new ImpuestoRenta(30000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 8%: " + iR);
assertTrue(c.calcularImpuesto() == 188.00);
}
@Test
public void tasa8False() {
ImpuestoRenta c = new ImpuestoRenta(30000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 8%: " + iR);
assertFalse(c.calcularImpuesto() != 188.00);
}
@Test
public void tasa8Equals() {
ImpuestoRenta c = new ImpuestoRenta(30000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 8%: " + iR);
assertEquals(c.calcularImpuesto() , 188.00);
}
@Test
public void tasa14True() {
ImpuestoRenta c = new ImpuestoRenta(50000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 14%: " + iR);
assertTrue(c.calcularImpuesto() == 1944.00);
}
@Test
public void tasa14False() {
ImpuestoRenta c = new ImpuestoRenta(50000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 14%: " + iR);
assertFalse(c.calcularImpuesto() != 1944.00);
}
@Test
public void tasa14Equals() {
ImpuestoRenta c = new ImpuestoRenta(50000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 14%: " + iR);
assertEquals(c.calcularImpuesto() , 1944.00);
}
@Test
public void tasa17True() {
ImpuestoRenta c = new ImpuestoRenta(120000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 17%: " + iR);
assertTrue(c.calcularImpuesto() == 12144.50);
@Test
public void tasa17False() {
ImpuestoRenta c = new ImpuestoRenta(120000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 17%: " + iR);
assertFalse(c.calcularImpuesto() != 12144.50);
}
@Test
public void tasa17Equals() {
ImpuestoRenta c = new ImpuestoRenta(120000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 17%: " + iR);
assertEquals(c.calcularImpuesto() , 12144.50);
}
@Test
public void tasa20True() {
ImpuestoRenta c = new ImpuestoRenta(200000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 20%: " + iR);
assertTrue(c.calcularImpuesto() == 26767.50);
}
@Test
public void tasa20False() {
ImpuestoRenta c = new ImpuestoRenta(200000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 20%: " + iR);
assertFalse(c.calcularImpuesto() != 26767.50);
}
@Test
public void tasa20Equals() {
ImpuestoRenta c = new ImpuestoRenta(200000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 20%: " + iR);
assertEquals(c.calcularImpuesto() , 26767.50);
}
@Test
public void tasa30True() {
ImpuestoRenta c = new ImpuestoRenta(300000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 30%: " + iR);
assertTrue(c.calcularImpuesto() == 56227.50);
}
@Test
public void tasa30False() {
ImpuestoRenta c = new ImpuestoRenta(300000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 30%: " + iR);
assertFalse(c.calcularImpuesto() != 56227.50);
}
@Test
public void tasa30Equals() {
ImpuestoRenta c = new ImpuestoRenta(300000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 30%: " + iR);
assertEquals(c.calcularImpuesto() , 56227.50);
}
}
1.2.2. Creación y Ejecución de una prueba unitaria de regular complejidad con JUnit
en IntelliJ
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
import java.util.ArrayList;
import java.util.Iterator;
public ShoppingCart() {
items = new ArrayList();
}
}
return balance;
}
import org.junit.jupiter.api.*;
class ProductTest {
private Product prod1;
@BeforeAll
static void setUpBeforeClass() throws Exception {
System.out.println("Ejecutando setUpBeforeClass");
}
@AfterAll
static void tearDownAfterClass() throws Exception {
System.out.println("Ejecutando tearDownAfterClass");
}
@BeforeEach
void setUp() throws Exception {
System.out.println("Ejecutando setUp");
prod1 = new Product("P001", "Plasma TV LG 32", "Plasma TV
with TDT Decod. and high resolution Screen", 699.99);
}
@AfterEach
void tearDown() throws Exception {
System.out.println("Ejecutando tearDown");
prod1 = null;
}
@Test
void testGetCode() {
System.out.println("getCode");
String expResult = "P001";
String result = prod1.getCode();
assertEquals(expResult, result);
}
@Test
public void testGetTitle() {
System.out.println("getTitle");
String expResult = "Plasma TV LG 32";
String result = prod1.getTitle();
assertEquals(expResult, result);
}
@Test
public void testGetDescription() {
System.out.println("getDescription");
String expResult = "Plasma TV with TDT Decod. and high
resolution Screen";
String result = prod1.getDescription();
assertEquals(expResult, result);
}
@Test
public void testGetPrice() {
System.out.println("getPrice");
double expResult = 699.99;
double result = prod1.getPrice();
assertEquals(expResult, result);
}
}
import org.junit.jupiter.api.*;
class ShoppingCartTest {
private ShoppingCart cart1;
private Product prod1;
@BeforeAll
static void setUpBeforeClass() throws Exception {
System.out.println("Ejecutando setUpBeforeClass");
}
@AfterAll
static void tearDownAfterClass() throws Exception {
System.out.println("Ejecutando tearDownAfterClass");
}
@BeforeEach
void setUp() throws Exception {
System.out.println("Ejecutando setUp");
cart1 = new ShoppingCart();
prod1 = new Product("P001", "Plasma TV LG 32", "Plasma TV
with TDT Decod. and high resolution Screen", 699.99);
cart1.addItem(prod1);
}
@AfterEach
void tearDown() throws Exception {
System.out.println("Ejecutando tearDown");
cart1 = null;
prod1 = null;
}
@Test
public void testGetBalance() {
System.out.println("getBalance");
double expResult = 699.99;
double result = cart1.getBalance();
assertEquals(expResult, result);
}
@Test
public void testAddItem() {
System.out.println("addItem");
Product prod2 = new Product("P002", "DVD player
Sanmsung",
"DVD player with remote control and programming
features", 39.99);
cart1.addItem(prod2);
assertEquals(2, cart1.getItemCount());
double expectedBalance = prod1.getPrice() +
prod2.getPrice();
assertEquals(expectedBalance, cart1.getBalance());
}
@Test
public void testGetItemCount() {
System.out.println("getItemCount");
int expResult = 1;
int result = cart1.getItemCount();
assertEquals(expResult, result);
}
@Test
public void testRemoveItem() {
System.out.println("removeItem");
cart1.removeItem(prod1);
assertEquals(0, cart1.getItemCount());
}
@Test
public void testEmpty() {
System.out.println("empty");
cart1.empty();
assertEquals(0, cart1.getItemCount());
}
}
1.2.3. Creación y Ejecución de una prueba unitaria avanzada con JUnit y Mockito en
IntelliJ
Mockito es un framework de prueba del código abierto para Java publicado bajo la
licencia MIT. El framework permite la creación de objetos dobles de prueba (objetos
simulados) en pruebas de unidad automatizada para el desarrollo guiado por pruebas
(TDD) o desarrollo dirigido por el comportamiento (BDD) (Stack Exchange Inc, 2016).
Utilizando la técnica Test Driven Development desarrolle un microservicio con sus respectivas
pruebas unitarias utilizando Mockito.
Para ello creamos un proyecto llamado “Tema2Sesion2”, agregando las siguientes
dependencias:
- controller
- model
- repo
- service
- service.impl
package pe.edu.cibertec.Tema2Sesion2.model;
import jakarta.persistence.*;
import lombok.Data;
import java.io.Serializable;
@Entity
@Data
@Table(name="persona")
public class Persona implements Serializable {
@Id
@GeneratedValue
@Column(name = "persona_id")
private int id;
@Column(name = "persona_nombre")
private String nombre;
@Column(name = "persona_apellido")
private String apellido;
@Column(name = "persona_direccion")
private String direccion;
@Column(name = "persona_telefono")
private String telefono;
@ManyToOne
@JoinColumn(name="distrito_id", nullable = false)
private Distrito distrito;
}
import jakarta.persistence.*;
import lombok.Data;
import java.io.Serializable;
@Data
@Entity
@Table(name="distrito")
public class Distrito implements Serializable {
@Id
@GeneratedValue
@Column(name = "distrito_id")
private int id;
@Column(name = "distrito_nombre")
private String nombre;
}
import org.springframework.data.jpa.repository.JpaRepository;
import pe.edu.cibertec.Tema2Sesion2.model.Persona;
import pe.edu.cibertec.Tema2Sesion2.model.Persona;
import java.util.List;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pe.edu.cibertec.Tema2Sesion2.model.Persona;
import pe.edu.cibertec.Tema2Sesion2.repo.IPersonaRepo;
import pe.edu.cibertec.Tema2Sesion2.service.IPersonaService;
import java.util.List;
@Service
@AllArgsConstructor
public class PersonaServiceImpl implements IPersonaService {
@Autowired
private IPersonaRepo personaRepo;
}
Genere la clase “PersonaController” en el paquete controller:
package pe.edu.cibertec.Tema2Sesion2.controller;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import pe.edu.cibertec.Tema2Sesion2.model.Persona;
import pe.edu.cibertec.Tema2Sesion2.service.IPersonaService;
import java.util.List;
@RestController
@AllArgsConstructor
@RequestMapping("api/v1/persona")
public class PersonaController {
@Autowired
private IPersonaService personaService;
@GetMapping
public ResponseEntity<?> listarPersonas() {
List<Persona> personas = personaService.listarPersonas();
return new ResponseEntity<>(personas, personas.size() > 0
? HttpStatus.OK : HttpStatus.NOT_FOUND);
}
@GetMapping(value = "/{id}")
public ResponseEntity<?> obtenerPersona(@PathVariable("id")
Integer id) {
return new
ResponseEntity<>(personaService.obtenerPersona(id),
HttpStatus.OK);
}
@PostMapping
public ResponseEntity<?> registrarPersona(@RequestBody
Persona persona) {
Persona newPersona =
personaService.registrarPersona(persona);
return new ResponseEntity<Persona>(newPersona,
HttpStatus.OK);
}
@PutMapping
public ResponseEntity<?> modificarPersona(@RequestBody
Persona persona) {
Persona newPersona =
personaService.modificarPersona(persona);
return new ResponseEntity<Persona>(newPersona,
HttpStatus.OK);
}
@DeleteMapping(value = "/{id}")
public ResponseEntity<?> eliminarPersona(@PathVariable("id")
Integer id) {
return new
ResponseEntity<>(personaService.eliminarPersona(id),
HttpStatus.OK);
}
}
Generar una carpeta llamada bd dentro de la carpeta resources, dentro de la carpeta bd crear
el archivo “tablas.sql” con el siguiente código:
IF NOT EXISTS
(SELECT 1 FROM distrito WHERE distrito_id = 1 AND
distrito_nombre = 'Lima')
BEGIN
INSERT distrito (distrito_id, distrito_nombre) VALUES (1,
'Lima')
END;
IF NOT EXISTS
(SELECT 1 FROM distrito WHERE distrito_id = 2 AND
distrito_nombre = 'Miraflores')
BEGIN
INSERT distrito (distrito_id, distrito_nombre) VALUES (2,
'Miraflores')
END;
IF NOT EXISTS
(SELECT 1 FROM distrito WHERE distrito_id = 3 AND
distrito_nombre = 'San Isidro')
BEGIN
INSERT distrito (distrito_id, distrito_nombre) VALUES (3,
'San Isidro')
END;
IF NOT EXISTS
(SELECT 1 FROM distrito WHERE distrito_id = 4 AND
distrito_nombre = 'Jesus Maria')
BEGIN
INSERT distrito (distrito_id, distrito_nombre) VALUES (4,
'Jesus Maria')
END;
IF NOT EXISTS
(SELECT 1 FROM distrito WHERE distrito_id = 5 AND
distrito_nombre = 'Pueblo Libre')
BEGIN
INSERT distrito (distrito_id, distrito_nombre) VALUES (5,
'Pueblo Libre')
END;
spring.application.name=pruebaunitaria
server.port=8080
spring.datasource.url=jdbc:sqlserver://localhost:1433;DatabaseNam
e=Tema2Sesion2;encrypt=false;trustServerCertificate=false;
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQ
LServerDriver
spring.datasource.username=sa
spring.datasource.password=1234
spring.jpa.show-sql=true
spring.datasource.driver-class-
name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.database-
platform=org.hibernate.dialect.SQLServer2016Dialect
spring.jpa.hibernate.ddl-auto=update
spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath:bd/tablas.sql
spring.sql.init.data-locations=classpath:bd/data.sql
Revisar en la BD que se haya generado las tablas distrito y persona, y dentro de la tabla distrito
visualizar que exista data:
Utilizando el software Postman genera los siguientes request, se adjunta el CURL para su
generación:
"nombre": "Lima"
}
}'
- Curl para el request “eliminarPersona”:
curl --location --
request DELETE 'http://localhost:8080/api/v1/persona/1'
Ahora implementaremos las pruebas unitarias respectivas con Mockito, para ello generamos la
clase PersonaServiceImplTest:
package pe.edu.cibertec.Tema2Sesion2.service.impl;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import
org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTes
t;
import pe.edu.cibertec.Tema2Sesion2.model.Distrito;
import pe.edu.cibertec.Tema2Sesion2.model.Persona;
import pe.edu.cibertec.Tema2Sesion2.repo.IPersonaRepo;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@WebMvcTest(PersonaServiceImplTest.class)
class PersonaServiceImplTest {
@InjectMocks
PersonaServiceImpl personaServiceImpl;
@Mock
IPersonaRepo personaRepo;
@BeforeEach
void setUp() {
personaServiceImpl = new PersonaServiceImpl(personaRepo);
}
@AfterEach
void tearDown() {
}
@Test
void listarPersonas() {
List<Persona> personas = generarListaPersonas();
when(personaRepo.findAll()).thenReturn(personas);
List<Persona> personasResponse =
personaServiceImpl.listarPersonas();
assertEquals(2, personasResponse.size());
}
@Test
void obtenerPersona() {
Persona persona = generarPersona();
when(personaRepo.findById(anyInt())).thenReturn(Optional.ofNullab
le(persona));
Persona personaResponse =
personaServiceImpl.obtenerPersona(1);
assertEquals(1, personaResponse.getId());
}
@Test
void registrarPersona() {
Persona persona = generarPersona();
when(personaRepo.save(any(Persona.class))).thenReturn(persona);
Persona personaResponse =
personaServiceImpl.registrarPersona(persona);
assertEquals(1, personaResponse.getId());
}
@Test
void modificarPersona() {
Persona persona = generarPersona();
when(personaRepo.save(any(Persona.class))).thenReturn(persona);
Persona personaResponse =
personaServiceImpl.modificarPersona(persona);
assertEquals(1, personaResponse.getId());
}
@Test
void eliminarPersona() {
doNothing().when(personaRepo).deleteById(anyInt());
String personaResponse =
personaServiceImpl.eliminarPersona(1);
assertEquals("Persona eliminada", personaResponse);
}
List generarListaPersonas(){
Distrito distrito = new Distrito();
distrito.setId(1);
distrito.setNombre("Lima");
return personas;
}
Persona generarPersona(){
Distrito distrito = new Distrito();
distrito.setId(1);
distrito.setNombre("Lima");
return persona;
}
}
package pe.edu.cibertec.Tema2Sesion2.controller;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import
org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTes
t;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import pe.edu.cibertec.Tema2Sesion2.model.Distrito;
import pe.edu.cibertec.Tema2Sesion2.model.Persona;
import
pe.edu.cibertec.Tema2Sesion2.service.impl.PersonaServiceImpl;
import java.util.ArrayList;
import java.util.List;
@WebMvcTest(PersonaControllerTest.class)
class PersonaControllerTest {
@InjectMocks
PersonaController personaController;
@Mock
PersonaServiceImpl personaServiceImpl;
@BeforeEach
void setUp() {
personaController = new
PersonaController(personaServiceImpl);
}
@AfterEach
void tearDown() {
}
@Test
void listarPersonas() {
List<Persona> personas = generarListaPersonas();
when(personaServiceImpl.listarPersonas()).thenReturn(personas);
ResponseEntity<?> responseEntity =
personaController.listarPersonas();
assertEquals(HttpStatus.OK,
responseEntity.getStatusCode());
@Test
void obtenerPersona() {
Persona persona = generarPersona();
when(personaServiceImpl.obtenerPersona(anyInt())).thenReturn(pers
ona);
ResponseEntity<?> responseEntity =
personaController.obtenerPersona(1);
assertEquals(HttpStatus.OK,
responseEntity.getStatusCode());
@Test
void registrarPersona() {
Persona persona = generarPersona();
when(personaServiceImpl.registrarPersona(any(Persona.class))).the
nReturn(persona);
ResponseEntity<?> responseEntity =
personaController.registrarPersona(persona);
assertEquals(HttpStatus.OK,
responseEntity.getStatusCode());
@Test
void modificarPersona() {
Persona persona = generarPersona();
when(personaServiceImpl.modificarPersona(any(Persona.class))).the
nReturn(persona);
ResponseEntity<?> responseEntity =
personaController.modificarPersona(persona);
assertEquals(HttpStatus.OK,
responseEntity.getStatusCode());
@Test
void eliminarPersona() {
when(personaServiceImpl.eliminarPersona(anyInt())).thenReturn("Pe
rsona eliminada");
personaController.eliminarPersona(1);
verify(personaServiceImpl,times(1)).eliminarPersona(anyInt());
}
List generarListaPersonas(){
Distrito distrito = new Distrito();
distrito.setId(1);
distrito.setNombre("Lima");
persona1.setApellido("myApellido1");
persona1.setDireccion("myDireccion1");
persona1.setTelefono("987654321");
persona1.setDistrito(distrito);
return personas;
}
Persona generarPersona(){
Distrito distrito = new Distrito();
distrito.setId(1);
distrito.setNombre("Lima");
return persona;
}
}
Resumen
1. TDD es una Técnica de Desarrollo basada en “testear” primero:
4. Un test tiene tres partes, que se identifican con las siglas AAA en inglés: Arrange (Preparar),
Act (Actuar), Assert (Afirmar).
5. JUnit es un framework, creado por Erich Gamma y Kent Beck, utilizado para automatizar
las pruebas unitarias en el lenguaje Java.
6. Mockito es una librería Java que permite simular el comportamiento de una clase de forma
dinámica.
Recursos
Pueden revisar los siguientes enlaces para ampliar los conceptos vistos en esta unidad:
o https://www.youtube.com/watch?v=V-cHmUwL_LU
o https://www.youtube.com/watch?v=NHi3V7ZGoEI
o https://www.youtube.com/watch?v=cXQhvrWvgE8
UNIDAD
2
FUNDAMENTOS DE BDD
LOGRO DE LA UNIDAD DE APRENDIZAJE
Al término de la unidad, el alumno asimila los conceptos y aplica Behavior Driven
Development (BDD) en una aplicación Java utilizando lenguaje Gherkin integrado con
Cucumber, Serenity y Rest Assured.
TEMARIO
3.1 Tema 5 : Pruebas funcionales con Selenium
3.1.1 : Selenium IDE
3.1.2 : Generación de script con Selenium
3.1.3 : Integrar script de Selenium con IntelliJ
ACTIVIDADES PROPUESTAS
BDD surge directamente del TDD o desarrollo guiado por pruebas. Pero a diferencia del
TDD, el BDD define las pruebas centradas en el usuario y el comportamiento del sistema
y no en las funcionalidades de éste. En otras palabras, el BDD describe las pruebas en un
lenguaje natural que entienden todos los equipos de un proyecto, y no únicamente los
programadores (Vergara, 2019)
El desarrollo guiado por comportamiento es perfecto para comunicarse con todos los
equipos durante el desarrollo de productos multifuncionales. Un equipo de
desarrolladores y analistas QA trabaja en el código mediante un lenguaje de
programación específico que en muchas ocasiones el cliente o cualquier otro equipo del
proyecto no es capaz de leer.
Antes de aplicar BDD es importante saber de dónde partimos, y que la información sea
común a todas las partes. Si hemos comentado que el BDD surge de la necesidad de que
todos los implicados entiendan de qué se habla, el que la información previa sea de
conocimiento general también es importante (Zapater, 2022)
Es un lenguaje específico de dominio que sirven para resolver problemas muy concretos.
Lo positivo de este lenguaje es que cuenta con los elementos Given-When-Then que se
han convertido en los principios de BDD básicos. Además de estos elementos hay
muchos otros que ayudan a especificar y detallar las acciones, pero la base podría
empezar por estos cinco:
Cuando haces una búsqueda en Google, como la consulta “ejemplos de BDD”, queremos
que el motor de búsqueda nos devuelva los resultados que respondan a esta búsqueda
concreta. Ahí tenemos nuestro Feature.
Feature: Como usuario web, quiero buscar en Google ejemplos de BDD para que
me devuelva resultados y respuestas.
Scenario: Búsqueda en Google.
Given: Buscador de Google.
When: Introducir la consulta “ejemplos de BDD”.
Then: Se muestran resultados de “ejemplos de BDD” (Zapater, 2022).
2.1.3. Cucumber
El primer paso es crear un fichero “.feature” donde añadiremos todos los tests cases de
nuestro caso de uso. Por cada test case, crearemos un escenario dentro de este fichero
feature. Este escenario será la implementación del test case del analista.
Estos ficheros, deberán ser almacenados en un proyecto Java, el cual nos permitirá la
ejecución de las pruebas y el almacenaje de todos los ficheros de pruebas necesarios,
como pueden ser los feature o los ficheros anexos que nos permitan realizar dichas
pruebas (como pueden ser los payloads de llamada).
Cucumber nos proporciona un informe, que será el detalle de la ejecución de todas las
pruebas que hemos realizado y el resultado de estas. En dicho informe veremos:
Testear y validar servicios REST en Java puede ser bastante complejo si no usamos
frameworks adecuados. Rest-Assured es un Java DSL (Domain Specific Language)
construido sobre la capa HTTP Builder que permite simplificar la construcción de test
sobre una API REST.
Este tipo de test se suelen ejecutar un paso después del proceso de integración continua,
consumiendo APIs REST después de que hayan sido desplegadas.
Cuando validamos un recurso REST, hay que fijar el foco en una serie de aspectos que
nos devuelven como respuesta las llamadas a una API:
Cada test debe fijar el foco en una única responsabilidad e incluir una única assertion.
Rest-assured ofrece una serie de características bastante útiles como sintaxis DSL,
validación Xpath, usar JsonPath, registrar parsers para MIME-Types, file upload, verificar
cabeceras HTTP o cookies (Rodríguez, 2013)
2.1.5. Serenity
Es una librería de código abierto que ayuda a escribir pruebas de aceptación automatizadas de
mayor calidad y más rápido. Sus principales características son:
¿Y Cucumber?
Cucumber está inmerso en Serenity, podemos hacer la analogía de que Serenity es el padre
de Cucumber. Estos dos los usamos cuando escribimos las “feature” en el lenguaje Gherkin.
El patrón Screen Play cumple con los principios SOLID y sus características son:
Es un patrón de diseño orientado al comportamiento BDD.
Presenta un alto desacoplamiento de la interfaz de usuario.
Propone trabajar en términos de tareas y no de interfaz de usuario.
El patrón Page Object no cumple con algunos de los principios SOLID recomendados para
realizar código flexible y mantenible.
Adaptado de Soto, M. (2019, Octubre 16). Automatización de Pruebas Funcionales: Serenity BDD+Screen Play+Java. Medium.
https://medium.com/@marcela.soto/automatizaci%C3%B3n-de-pruebas-funcionales-serenity-bdd-screen-play-java-942be8217fca
Capas Principales
1) Features: En esta capa se escriben los archivos “.feature”, estos describen las
características y escenarios a probar haciendo uso del lenguaje Gherkin.
2) Runners: Contiene los archivos runner encargados de ejecutar los escenarios de la
feature. Se usan las anotaciones @RunWhit() @CucumberOptions() para especificar
qué ejecutar y cómo hacerlo.
3) Step Definitions: Gestiona los code snippets de los steps definidos con las palabras
Given, When, Then en los escenarios de los archivos “.feature”.
4) Tasks: Esta clase contiene las clases que son el corazón del patrón de screenplay=Las
tareas. Las tareas son acciones de alto nivel que definen un conjunto de
interacciones en el lenguaje de negocio.
Capas Transversales
1) Models: Los objetos de negocio son una abstracción de un ente real o virtual,
modelado mediante atributos y métodos.
2) Utils: Contiene clases con los métodos unitarios que pueden ser utilizados
libremente por otras clases del proyecto.
3) Exceptions: Esta es una capa muy importante que tiene como objetivo dar manejo
a los errores que pueden presentarse en la aplicación. Es una capa sensible al
negocio y particular a cada proyecto (Soto, 2019).
Resumen
1. BDD (Behavior Driven Development) busca la colaboración y entendimiento entre
desarrolladores, gestores de proyecto y equipo de negocio.
2. BDD surge directamente del TDD o desarrollo guiado por pruebas. Pero a diferencia del
TDD, el BDD define las pruebas centradas en el usuario y el comportamiento del sistema y
no en las funcionalidades de éste.
3. Gherkin es el lenguaje que entiende Cucumber, es un DSL legible para gente no técnica,
que permite definir el comportamiento del software sin detallar como está implementado.
4. Rest Assured es una tecnología de código abierto muy utilizada para las pruebas de
automatización de API REST, que se basa en una biblioteca basada en Java.
5. Serenity es una librería de código abierto que ayuda a escribir pruebas de aceptación
automatizadas de mayor calidad y más rápido.
Recursos
Pueden revisar los siguientes enlaces para ampliar los conceptos vistos en esta unidad:
o https://www.youtube.com/watch?v=sQvAWUVoFCs
o https://www.youtube.com/watch?v=gNTOF4Th7X0
o https://www.youtube.com/watch?v=hZ2FaJBxRQ8
o https://www.youtube.com/watch?v=81zQrZzKWOk
Utilizando la técnica BDD implemente las pruebas integrales con Rest Assured, Serenity, Gherkin
y Cucumber para validar el api de códigos postales para países (api.zippopotam.us).
Para ello creamos un proyecto llamado “Tema4Ejercicio1” sin agregar dependencias:
Elimine de los paquetes src.main.java y src.main.resources todas las clases y recursos generados.
<serenity.cucumber.version>2.6.0</serenity.cucumber.version>
<rest.assured.version>5.3.0</rest.assured.version>
</properties>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<artifactId>serenity-junit</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-rest-assured</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay-rest</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-cucumber6</artifactId>
<version>${serenity.cucumber.version}</version>
<scope>test</scope>
</dependency>
<exclusion>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>20</source>
<target>20</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
package cucumber;
import io.cucumber.junit.CucumberOptions;
import net.serenitybdd.cucumber.CucumberWithSerenity;
import org.junit.runner.RunWith;
@RunWith(CucumberWithSerenity.class)
@CucumberOptions(
plugin = {"pretty"},
features = "classpath:features"
)
public class CucumberTestSuite {}
package cucumber.codigopostal;
import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Step;
}
}
Genere la clase “UbicacionResponse” en el paquete src.test.java.cucumber.codigopostal con el
siguiente código:
package cucumber.codigopostal;
package cucumber.codigopostal;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import net.thucydides.core.annotations.Steps;
@Steps
CodigoPostalAPI codigoPostalAPI;
codigoPostalAPI.buscarUbicacionPorCodigoPostalYPais(postCode,
country);
}
<configuration>
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -
%msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Utilizando la técnica BDD implemente las pruebas integrales con Rest Assured, Serenity, Gherkin
y Cucumber para consultar usuarios con GO REST (https://gorest.co.in/).
- java.cucumber.usuario.stepDef
- java.cucumber.utils
- resources.features.usuario
- resources.JSON.Schema
<groupId>pe.edu.cibertec</groupId>
<artifactId>Tema4Ejercicio2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Tema4Ejercicio2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>20</java.version>
<serenity.version>2.6.0</serenity.version>
<serenity.cucumber.version>2.6.0</serenity.cucumber.version>
<logback.version>1.4.7</logback.version>
<rest.assured.version>5.3.0</rest.assured.version>
<groovy.version>3.0.6</groovy.version>
</properties>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-junit</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-rest-assured</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay-rest</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-cucumber6</artifactId>
<version>${serenity.cucumber.version}</version>
<scope>test</scope>
</dependency>
</exclusion>
<exclusion>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/io.rest-
assured/json-schema-validator -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-schema-validator</artifactId>
<version>${rest.assured.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>${groovy.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-xml</artifactId>
<version>${groovy.version}</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>20</source>
<target>20</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
import io.cucumber.junit.CucumberOptions;
import net.serenitybdd.cucumber.CucumberWithSerenity;
import org.junit.runner.RunWith;
@RunWith(CucumberWithSerenity.class)
@CucumberOptions(
tags = "@Test",
plugin = {"pretty"},
features = {"src/test/resources/features/usuario"},
glue = {"cucumber.usuario.stepDef"}
)
public class CucumberTestSuite {}
package cucumber;
import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Step;
import cucumber.utils.Constantes;
@Step("Obtener usuarios")
public void obtenerUSuarios(String nombre, String email,
String genero, String estado) {
SerenityRest.given()
.queryParam("name", nombre)
.queryParam("email", email)
.queryParam("gender", genero)
.queryParam("status", estado);
}
}
package cucumber.utils;
package cucumber.usuario.stepDef;
import io.cucumber.java.en.Then;
import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Steps;
import cucumber.UsuarioAPI;
package cucumber.usuario.stepDef;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.restassured.module.jsv.JsonSchemaValidator;
import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Steps;
import cucumber.UsuarioAPI;
import cucumber.utils.Constantes;
import java.io.File;
.body(JsonSchemaValidator.matchesJsonSchema(jsonSchema));
}
<configuration>
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -
%msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
@Test
Feature: Obtener usuarios
| email |
| @johnston |
2.2.3. Creación y Ejecución de una prueba integral avanzada con Gherkin, Cucumber,
Serenity y Rest Assured en IntelliJ
Utilizando la técnica BDD implemente las pruebas integrales con Rest Assured, Serenity, Gherkin
y Cucumber para validar usuarios con GO REST.
Para ello creamos un proyecto llamado “Tema4Ejercicio3”sin agregar dependencias y realizamos
todos los ajustes realizados en el ejercicio anterior.
package cucumber;
import cucumber.utils.Constantes;
import io.restassured.http.ContentType;
import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Step;
import java.io.File;
@Step("Obtener usuarios")
public void obtenerUSuarios(String nombre, String email,
String genero, String estado) {
SerenityRest.given()
.queryParam("name", nombre)
.queryParam("email", email)
.queryParam("gender", genero)
.queryParam("status", estado);
}
@Step("Crear usuario")
public void crearUsuario(String json) {
SerenityRest.given()
.header("Authorization", "Bearer " +
Constantes.TOKEN)
.contentType(ContentType.JSON)
.body(json);
}
@Step("Eliminar usuario")
public void eliminarUsuario(String id, boolean withToken) {
SerenityRest.given()
.header("Authorization", withToken ? "Bearer " +
Constantes.TOKEN : "")
.pathParam("user_id", id);
}
}
package cucumber.utils;
package cucumber.utils;
stringBuilder.append(characters.charAt((int)
(Math.random() * nameLength)));
counter++;
}
jsonString = "{";
jsonString += "\"name\": \"" + stringBuilder.toString() +
"\",";
jsonString += "\"gender\": \"male\",";
jsonString += "\"email\": \"" + stringBuilder.toString()
+ "@gmail.com\",";
jsonString += "\"status\": \"inactive\"";
jsonString += "}";
return jsonString;
}
return jsonString;
}
}
package cucumber.usuario.stepDef;
import cucumber.UsuarioAPI;
import cucumber.utils.Constantes;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Then;
import io.restassured.module.jsv.JsonSchemaValidator;
import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Steps;
import java.io.File;
}
@And("validar json schema response error")
public void validarJsonSchemaResponseError() {
File jsonSchema = new File(Constantes.JSON_SCHEMA +
"/ResponseError.json");
SerenityRest.then()
.assertThat()
.body(JsonSchemaValidator.matchesJsonSchema(jsonSchema));
}
}
package cucumber.usuario.stepDef;
import com.google.gson.Gson;
import cucumber.UsuarioAPI;
import cucumber.utils.Constantes;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.restassured.module.jsv.JsonSchemaValidator;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Steps;
import java.io.File;
.body(JsonSchemaValidator.matchesJsonSchema(jsonSchema));
package cucumber.usuario.stepDef;
import cucumber.UsuarioAPI;
import cucumber.utils.Constantes;
import cucumber.utils.Payload;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.restassured.module.jsv.JsonSchemaValidator;
import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Steps;
import java.io.File;
usuarioAPI.crearUsuario(payload.randomBodyRequestPostUser());
}
.body(JsonSchemaValidator.matchesJsonSchema(jsonSchema));
}
SerenityRest.given().body(payload.randomBodyRequestPostUser());
}
}
package cucumber.usuario.stepDef;
import cucumber.UsuarioAPI;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Steps;
usuarioAPI.eliminarUsuario(jsonPathEvaluator.get("[0].id").toStri
ng(), true);
}
@Test
Feature: Actualizar usuario
@Test
Feature: Crear usuario
"Authentication failed"
And validar json schema response error
@Test
Feature: Eliminar usuario
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"gender": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"id",
"name",
"email",
"gender",
"status"
]
}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"email": {
"type": "string"
},
"name": {
"type": "string"
},
"gender": {
"type": "string"
},
"status": {
"type": "string"
},
"id": {
"type": "integer"
}
},
"required": [
"email",
"name",
"gender",
"status",
"id"
]
}
{
"user_name_invalid": "update"
}
Resumen
1. El patrón Screenplay es una forma poderosa de escribir pruebas de aceptación
automatizadas altamente reutilizables y fáciles de mantener que también actúan como
documentación viva. El guion también se destaca en la creación de componentes de
prueba que puede usar fácilmente en otras partes en otras pruebas, lo que ahorra tiempo
tanto en la redacción como en la ejecución de la prueba.
Recursos
Pueden revisar los siguientes enlaces para ampliar los conceptos vistos en esta unidad:
o https://www.youtube.com/watch?v=sQvAWUVoFCs
o https://www.youtube.com/watch?v=gNTOF4Th7X0
o https://www.youtube.com/watch?v=hZ2FaJBxRQ8
o https://www.youtube.com/watch?v=81zQrZzKWOk
UNIDAD
3
FUNDAMENTOS DE PRUEBAS FUNCIONALES
WEB
LOGRO DE LA UNIDAD DE APRENDIZAJE
Al término de la unidad, el alumno implementa scripts de automatización de pruebas
funcionales web en una aplicación Java utilizando Selenium.
TEMARIO
ACTIVIDADES PROPUESTAS
Componentes de Selenium
Selenium Remote Control: RC hace posible escribir pruebas automatizadas para aplicaciones
web, en cualquier lenguaje de programación lo que permite una mejor integración de Selenium
a entornos de prueba existentes.
Selenium WebDriver: Selenium WebDriver acepta comandos (enviados en Selenese o vía el API
de cliente) y los envía a un navegador. Esto se implementa a través de un controlador del
navegador específico para cada navegador que envía los comandos y trae los resultados de
regreso.
Seleccionar “Añadir”:
Figura 3.26: Agregar variable de entorno del driver de Chrome para Selenium
Descargar el driver de edge del siguiente enlace, cotejar según la versión de tu browser:
https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
Para integrar el script de Selenium con IntelliJ, primero se debe realizar una grabación utilizando
Selenium IDE, esta grabación generará un script de prueba en código Selenese, este código
puede ser exportado como una clase JUnit Java, esta clase Java es la que se importa en IntelliJ.
Resumen
1. Selenium es un entorno de pruebas que se utiliza para comprobar si el software funciona
correctamente. Esta herramienta permite: grabar, editar y depurar casos de pruebas
funcionales web.
Recursos
Pueden revisar los siguientes enlaces para ampliar los conceptos vistos en esta unidad:
o https://www.youtube.com/watch?v=pKwNb8tqjf8
o https://www.youtube.com/watch?v=KskSQI5ZPVM
o https://www.youtube.com/watch?v=PIZVN6xPJDI
Implemente las pruebas funcionales web con Selenium para realizar el logueo de la página de
demo: https://www.saucedemo.com/
Se abrirá la siguiente pantalla, luego seleccionar la opción “Record a new test in a new project”:
Empezamos la grabación de la parte del login, luego del login detenemos la grabación
presionando de nuevo el icono de Selenium, observamos que en la parte inferior dice “Selenium
IDE is recording…”:
Va a pedir un nombre para guardar el test, colocamos PruebaLogin y luego presionamos OK:
Probamos el proyecto, para ello lo ejecutamos con la opción “Run current test”:
Guardamos el proyecto para poder volver a cargarlo cuando queramos con la opción “Save
project”:
Por defecto se graba con el nombre del proyecto y con la extensión .side, en este caso
“LoginDemoSauce”:
Luego, exportamos nuestro código con click derecho sobre el nombre del test:
Elimine de los paquetes src.main.java y src.main.resources todas las clases y recursos generados.
También, elimine la clase Tema6Ejercicio1ApplicationTests.java y en su lugar copie el archivo
PruebaLoginTest.java exportado de Selenium, cargará con errores.
Al archivo pom.xml agregar las dependencias de Selenium y Junit de tal forma que quede de la
siguiente forma:
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>pe.edu.cibertec</groupId>
<artifactId>Tema6Ejercicio1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Tema6Ejercicio1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>20</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.10.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
A la clase PruebaLoginTest.java, realizar los siguientes ajustes (para eliminar los errores) de tal
forma que quede de la siguiente forma:
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import java.util.*;
Como práctica, generar los tests en java que corran tanto en Firefox como en Edge, para ello,
abrir el Selenium IDE desde Firefox, importar el proyecto “LoginDemoSauce.side” (que
exportamos desde Chrome), ejecutar y validar que el test se ejecute correctamente, luego
exportarlo como JUnit, automáticamente se exportará con el driver de Firefox de Selenium
(geckodriver); al ejecutar el test, se cargará con Firefox. Hacer los mismos pasos con Edge.
Utilizando la técnica BDD implemente las pruebas funcionales web con Selenium, Serenity,
Gherkin y Cucumber para realizar el logueo y la compra de un producto de la página de demo:
https://www.saucedemo.com/
Elimine de los paquetes src.main.java y src.main.resources todas las clases y recursos generados.
También, elimine la clase Tema6Ejercicio2ApplicationTests.java y en su lugar copie el archivo
PruebaLoginTest.java exportado de Selenium, cargará con errores.
Al archivo pom.xml agregar las dependencias de Selenium y Junit de tal forma que quede de la
siguiente forma:
<serenity.cucumber.version>2.6.0</serenity.cucumber.version>
<maven.compiler.source>20</maven.compiler.source>
<maven.compiler.target>20</maven.compiler.target>
</properties>
<dependencies>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-rest-assured</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay-webdriver</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-cucumber6</artifactId>
<version>${serenity.cucumber.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- java.cucumber.stepdefs
- java.cucumber.pages
- resources.features.login
package cucumber.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
/**
* A class that represents all the common features between all
the pages in the Sauce Demo website
*/
public abstract class Pagina {
// All the web elements needed and the web driver declaration
private WebDriver driver;
private By appLogo = new By.ByClassName("app_logo");
private By pageTitle = new By.ByClassName("title");
private By footerImage = new By.ByClassName("footer_robot");
private By footerText = new By.ByClassName("footer_copy");
private By sideMenuButton = new By.ById("react-burger-menu-
btn");
private By exitSideMenuButton = new By.ById("react-burger-
cross-btn");
private By cartIcon = new
By.ByClassName("shopping_cart_link");
private By twitterIcon = new By.ByLinkText("Twitter");
private By facebookIcon = new By.ByLinkText("Facebook");
private By linkedinIcon = new By.ByLinkText("LinkedIn");
/**
* Method which returns the webdriver for other pages to use
in their own methods
*/
public WebDriver getDriver() {
try {
return driver;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method which determines whether the page logo is present
*/
public Boolean isLogoPresent() {
try {
return
driver.findElement(appLogo).getAttribute("class").equals("app_log
o");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
/**
* Method for checking if the footer image is present in the
page
*/
public Boolean isFooterImagePresent() {
try {
return
driver.findElement(footerImage).getAttribute("alt").equals("Swag
Bot Footer");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for checking if the footer text is present
*/
public Boolean isFooterTextPresent() {
try {
return
driver.findElement(footerText).getAttribute("class").equals("foot
er_copy");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for clicking the side menu, which is to be used to
click the other options within it
*/
public void clickSideMenu() {
try {
driver.findElement(sideMenuButton).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for closing the side menu
*/
public void closeSideMenu() {
try {
driver.findElement(exitSideMenuButton).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for clicking and choosing the 'All items' option
*/
public void clickAllItemsFromSideMenu() {
try {
By sideMenuOptionsLink = new
By.ById("inventory_sidebar_link");
driver.findElement(sideMenuOptionsLink).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for clicking and choosing the 'About' option
*/
public void clickAboutFromSideMenu() {
try {
By sideMenuOptionsLink = new
By.ById("about_sidebar_link");
driver.findElement(sideMenuOptionsLink).click();
} catch (Exception e){
e.printStackTrace();
}
}
/**
* Method for clicking the 'Log out' option
*/
public void clickLogoutFromSideMenu() {
try {
By sideMenuOptionsLink = new
By.ById("logout_sidebar_link");
driver.findElement(sideMenuOptionsLink).click();
} catch (Exception e){
e.printStackTrace();
}
}
/**
* Method for choosing the 'App state' option
*/
public void clickResetAppStateFromSideMenu() {
try {
By sideMenuOptionsLink = new
By.ById("reset_sidebar_link");
driver.findElement(sideMenuOptionsLink).click();
} catch (Exception e){
e.printStackTrace();
}
}
/**
* Method for clicking the cart Icon
*/
public void clickCartIcon() {
try {
driver.findElement(cartIcon).click();
} catch (Exception e){
e.printStackTrace();
}
}
/**
* Method for clicking the twitter icon in the footer
*/
public void clickTwitterIcon() {
try {
driver.findElement(twitterIcon).click();
} catch (Exception e){
e.printStackTrace();
}
}
/**
* Method for clicking the facebook icon in the footer
*/
public void clickFacebookIcon() {
try {
driver.findElement(facebookIcon).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for clicking the linked in icon in the footer
*/
public void clickLinkedinIcon() {
try {
driver.findElement(linkedinIcon).click();
} catch (Exception e){
e.printStackTrace();
}
}
/**
* Method for returning the current url that the driver is in
*/
public String getCurrentURL() {
try {
return driver.getCurrentUrl();
} catch (Exception e){
e.printStackTrace();
}
return null;
}
}
package cucumber.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
/**
// Accessing the web elements from the login page that could
be useful for the testers
private By usernameTextBox = new By.ById("user-name");
private By passwordTextBox = new By.ById("password");
private By loginButton = new By.ById("login-button");
/**
* Method for entering a series of string characters in the
username text box
*/
public void ingresarUsername(String username) {
try {
this.getDriver().findElement(usernameTextBox).sendKeys(username);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for entering a series of string characters in the
password text box
*/
public void ingresarPassword(String password) {
try {
this.getDriver().findElement(passwordTextBox).sendKeys(password);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for clicking the login button in the login page
*/
public void clickBotonLogin() {
try {
this.getDriver().findElement(loginButton).click();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package cucumber.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
/**
* A class that represents the inventory page in the Sauce Demo
website
*/
public class PaginaInventario extends Pagina {
this.getDriver().get("https://www.saucedemo.com/inventory.html");
}
/**
* This method allows to click any item image by passing in
an index
*/
public void clickItemImageAtIndex(int index) {
try {
this.getDriver().findElements(itemImages).get(index).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* This method allows to get any item's image source by
passing in an index
*/
public String getItemImageSourceAtIndex(int index) {
try {
return
this.getDriver().findElements(itemImages).get(index).getAttribute
("source");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
this.getDriver().findElements(itemNames).get(index).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for getting any item names in the inventory page by
passing in an index
*/
public String getItemNameAtIndex(int index) {
try {
return
this.getDriver().findElements(itemNames).get(index).getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for getting any item prices in the inventory page
by passing in an index
*/
public String getPriceAtIndex(int index) {
try {
return
this.getDriver().findElements(itemPrices).get(index).getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for getting an item descriptions in the inventory
page by passing in an index
*/
public String getItemDescriptionAtIndex(int index) {
try {
return
this.getDriver().findElements(itemDescription).get(index).getText
();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for clicking any add to cart ore remove from cart
button by passing in an index
*/
public void clickAddToCartOrRemoveButtonAtIndex(int index) {
try {
this.getDriver().findElements(addToCartOrRemoveButtons).get(index
).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for sorting the inventory page by name from A to Z
*/
public void filterPageByNameAToZ() {
try {
this.getDriver().findElement(filterButton).click();
this.getDriver().findElements(filterOptions).get(0).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for sorting the inventory page by name from Z to A
*/
public void filterPageByNameZToA() {
try {
this.getDriver().findElement(filterButton).click();
this.getDriver().findElements(filterOptions).get(1).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for sorting the inventory page by price from lowest
to highest
*/
public void filterPageByPriceLowToHigh() {
try {
this.getDriver().findElement(filterButton).click();
this.getDriver().findElements(filterOptions).get(2).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for sorting the inventory page by price from
highest to lowest
*/
public void filterPageByPriceHighToLow() {
try {
this.getDriver().findElement(filterButton).click();
this.getDriver().findElements(filterOptions).get(3).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for getting any button names in the inventory page
by passing in its index
*/
public String getButtonNameAtIndex(int index) {
try {
return
this.getDriver().findElements(addToCartOrRemoveButtons).get(index
).getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for getting the shopping cart badge number
*/
public String getShoppingCartBadge() {
try {
return
this.getDriver().findElement(shoppingCartBade).getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
package cucumber.stepdefs;
import cucumber.pages.PaginaInventario;
import cucumber.pages.PaginaLogin;
import io.cucumber.java.After;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.junit.jupiter.api.Assertions;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.concurrent.TimeUnit;
@After
public void tearDown(){
if(driver != null){
driver.quit();
System.out.println("tearDown");
}
}
}
package cucumber;
import io.cucumber.junit.CucumberOptions;
import net.serenitybdd.cucumber.CucumberWithSerenity;
import org.junit.runner.RunWith;
@RunWith(CucumberWithSerenity.class)
@CucumberOptions(
plugin = {"pretty"},
features = {"src/test/resources/features"}
)
public class CucumberTestSuite {}
Examples:
| username |
|"standard_user" |
|"problem_user" |
|"performance_glitch_user"|
<configuration>
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -
%msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Utilizando la técnica BDD implemente las pruebas funcionales web con Selenium, Serenity,
Gherkin y Cucumber para realizar el logueo y la compra de un producto de la página de demo:
https://www.saucedemo.com/
Abrimos Chrome y hacemos click en el ícono de Selenium IDE, luego seleccionar la opción
“Record a new test in a new project” y colocamos de nombre: CompraDemoSauce y
presionamos OK. Luego, agregamos la url de saucedemo: https://www.saucedemo.com/ y
presionamos “START RECORDING”:
Va a pedir un nombre para guardar el test, colocamos PruebaCompra y luego presionamos OK.
Probamos el proyecto, para ello lo ejecutamos con la opción “Run current test”, en caso haya
errores, hacemos los ajustes correspondientes, luego observamos que se ejecuta el proyecto de
forma exitosa:
Guardamos el proyecto para poder volver a cargarlo cuando queramos con la opción “Save
project”, lo grabamos con el nombre “CompraDemoSauce.side”. Luego, exportamos nuestro
código con click derecho sobre el nombre del test:
Al archivo pom.xml agregar las dependencias de Selenium y Junit de tal forma que quede de la
siguiente forma:
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.10.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
A la clase PruebaCompraTest.java, realizar los siguientes ajustes (para eliminar los errores) de
tal forma que quede de la siguiente forma:
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import java.util.*;
test=\"username\"]")).click();
driver.findElement(By.cssSelector("*[data-
test=\"username\"]")).sendKeys("standard_user");
driver.findElement(By.cssSelector("*[data-
test=\"password\"]")).click();
driver.findElement(By.cssSelector("*[data-
test=\"password\"]")).sendKeys("secret_sauce");
driver.findElement(By.cssSelector("*[data-test=\"login-
button\"]")).click();
driver.findElement(By.cssSelector("*[data-test=\"add-to-cart-
sauce-labs-backpack\"]")).click();
driver.findElement(By.linkText("1")).click();
driver.findElement(By.cssSelector("*[data-
test=\"checkout\"]")).click();
driver.findElement(By.cssSelector("*[data-
test=\"firstName\"]")).click();
driver.findElement(By.cssSelector("*[data-
test=\"firstName\"]")).sendKeys("henry");
driver.findElement(By.cssSelector("*[data-
test=\"lastName\"]")).click();
driver.findElement(By.cssSelector("*[data-
test=\"lastName\"]")).sendKeys("ilizarbe");
driver.findElement(By.cssSelector("*[data-
test=\"postalCode\"]")).click();
driver.findElement(By.cssSelector("*[data-
test=\"postalCode\"]")).sendKeys("0200");
driver.findElement(By.cssSelector("*[data-
test=\"continue\"]")).click();
driver.findElement(By.cssSelector("*[data-
test=\"finish\"]")).click();
driver.findElement(By.cssSelector("*[data-test=\"back-to-
products\"]")).click();
}
}
<serenity.cucumber.version>2.6.0</serenity.cucumber.version>
<maven.compiler.source>20</maven.compiler.source>
<maven.compiler.target>20</maven.compiler.target>
</properties>
<dependencies>
<!-- Dependencias de Serenity -->
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-core</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-junit</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-rest-assured</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay-webdriver</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-cucumber6</artifactId>
<version>${serenity.cucumber.version}</version>
<scope>test</scope>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package cucumber.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
/**
* A class that represents the inventory item page in the Sauce
Demo website
*/
public class PaginaInventarioItem extends Pagina {
/**
* Method for returning the current item selected in the
inventory item page
*/
public String getItemName() {
try {
return
this.getDriver().findElement(itemName).getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for getting the source of the image of the current
item selected in the inventory item page
*/
public String getItemImageSource() {
try {
return
this.getDriver().findElement(itemImage).getAttribute("source");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for getting the description of the current item
selected in the inventory item page
*/
public String getItemDescription() {
try {
return
this.getDriver().findElement(itemDescription).getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for getting the price of the current item selected
in the inventory item page
*/
public String getItemPrice() {
try {
return
this.getDriver().findElement(itemPrice).getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for clicking the add to cart or remove from cart
button depending on its current state
*/
public void clickAddToCartOrRemoveButton() {
try {
this.getDriver().findElements(inventoryPageButtons).get(1).click(
);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for clicking the back to products button so users
can go back to the all items page
*/
public void clickBackToProductsButton() {
try {
this.getDriver().findElement(backToProductsButton).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for returning the name of the current state of the
button
*/
public String getAddToCartOrRemoveButtonName() {
try {
return
this.getDriver().findElements(inventoryPageButtons).get(1).getTex
t();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for getting the number in shopping cart badge
*/
public String getShoppingCartBadge() {
try {
return
this.getDriver().findElement(shoppingCartBade).getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
package cucumber.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import java.util.List;
/**
* A class that represents the cart page in the Sauce Demo
website
*/
public class PaginaCarrito extends Pagina {
// Accessing the web elements from the cart page that could
be useful for the testers
private By cartItems = new
By.ByClassName("inventory_item_name");
private By cartItemPrice = new
By.ByClassName("inventory_item_price");
private By continueShopping = new By.ById("continue-
shopping");
private By checkout = new By.ById("checkout");
private By remove = new By.ByClassName("btn");
this.getDriver().get("https://www.saucedemo.com/cart.html");
}
/**
* A method for clicking a specific item's name in the cart,
done through passing in an index
*/
public void clickItemAtIndex(int index) {
try {
List<WebElement> cartItemNames =
this.getDriver().findElements(cartItems);
cartItemNames.get(index).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* A method for returning an item's price based off the index
(position in the cart) given
*/
public String getItemPriceAtIndex(int index) {
try {
List<WebElement> cartItemPrices =
this.getDriver().findElements(cartItemPrice);
return cartItemPrices.get(index).getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* A method for returning an item's name based off the index
(position in the cart) given
*/
@SuppressWarnings("UnusedReturnValue")
public String getItemNameAtIndex(int index) {
try {
return
this.getDriver().findElements(cartItems).get(index).getText();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* A method for removing an item at a specific position in
the cart
*/
public void clickRemoveItemAtIndex(int index) {
try {
List<WebElement> removeButtons =
this.getDriver().findElements(remove);
removeButtons.get(index).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* A method for getting the current amount of items in the
cart
*/
public Integer getCartSize() {
try {
return
this.getDriver().findElements(cartItems).size();
} catch (Exception e) {
e.printStackTrace();
}
return null;
/**
* A method for allowing to take the user to go back to the
inventory page and continue shopping
*/
public void clickContinueShoppingButton() {
try {
this.getDriver().findElement(continueShopping).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* A method for allowing the user to go to the checkout page
*/
public void clickCheckoutButton() {
try {
this.getDriver().findElement(checkout).click();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package cucumber.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
/**
* A class that represents the checkout stage one page in the
Sauce Demo website
*/
public class PaginaPagar extends Pagina {
/**
* Method for entering a string of choice into the first name
text box
*/
public void enterFirstNameInTextBox(String firstName) {
try {
this.getDriver().findElement(firstNameTextBox).sendKeys(firstName
);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for entering a string of choice into the last name
text box
*/
public void enterLastNameInTextBox(String lastName) {
try {
this.getDriver().findElement(lastNameTextBox).sendKeys(lastName);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for entering a string of choice into the zipcode
text box
*/
public void enterZipCodeInTextBox(String zipCode) {
try {
this.getDriver().findElement(zipCodeTextBox).sendKeys(zipCode);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for getting the current strings inserted inside the
first name text box
*/
public String getStringFromFirstNameTextBox() {
try {
return
this.getDriver().findElement(firstNameTextBox).getAttribute(“valu
e”);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for getting the current strings inserted inside the
last name text box
*/
public String getStringFromLastNameTextBox() {
try {
return
this.getDriver().findElement(lastNameTextBox).getAttribute(“value
”);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for getting the current strings inserted inside the
zipcode text box
*/
public String getStringFromZipCodeTextBox() {
try {
return
this.getDriver().findElement(zipCodeTextBox).getAttribute(“value”
);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Method for clicking the cancel check out button
*/
public void clickCancelButton() {
try {
this.getDriver().findElement(cancelButton).click();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method for clicking the continue check out button
*/
public void clickContinueButton() {
try {
this.getDriver().findElement(continueButton).click();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package cucumber.stepdefs;
import cucumber.pages.*;
import io.cucumber.java.After;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.junit.jupiter.api.Assertions;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
/*
@Before
public void setup() {
System.setProperty("webdriver.chrome.driver",
"src/test/resources/chromedriver.exe");
System.out.println("setup");
}*/
Assertions.assertEquals("https://www.saucedemo.com/inventory-
item.html?id=1", paginaInventarioItem.getCurrentURL());
}
//CONTINUE SHOPPING
@When("Hago clic en 'continue shopping' en la pagina del
carrito")
public void clicEnContinueShoppingEnPaginaCarrito() {
paginaCarrito = new PaginaCarrito(driver);
paginaCarrito.clickContinueShoppingButton();
Assertions.assertEquals("https://www.saucedemo.com/inventory.html
", paginaInventario.getCurrentURL());
}
//CHECKOUT
@When("Hago clic en 'Checkout' en la pagina del carrito")
public void clicEnCheckoutEnPaginaCarrito() {
paginaCarrito = new PaginaCarrito(driver);
paginaCarrito.clickCheckoutButton();
}
Assertions.assertEquals("https://www.saucedemo.com/checkout-step-
one.html", paginaPagar.getCurrentURL());
}
@After
public void tearDown(){
if(driver != null){
driver.quit();
System.out.println("tearDown");
}
}
}
carrito
Then Volvere a la pagina de inventario
Resumen
1. Selenium se puede integrar con Gherkin y Cucumber.
2. Selenium IDE te ayuda a generar código en lenguajes como Java, Python, C#, Ruby,
Javascript, facilitando bastante, la automatización de pruebas funcionales web.
Recursos
Pueden revisar los siguientes enlaces para ampliar los conceptos vistos en esta unidad:
o https://www.youtube.com/watch?v=2Xo6AmogvI0
o https://www.youtube.com/watch?v=mQ4Vc8gh_0U&list=PLB_1vdQajX8wLGAqJTgCTYdSn
3yAtZOCm
o https://www.youtube.com/watch?v=m6bQVAd6k1I
UNIDAD
4
FUNDAMENTOS DE PRUEBAS FUNCIONALES
PARA MOVILES
LOGRO DE LA UNIDAD DE APRENDIZAJE
Al término de la unidad, el alumno implementa scripts de automatización de pruebas
funcionales móviles en una aplicación Java utilizando Appium.
TEMARIO
4.1 Tema 7 : Pruebas funcionales con Appium
4.1.1 : Appium
4.1.2 : Generación de script con Appium
4.1.3 : Integrar script de Appium con Android Studio
4.2 Tema 8 : Pruebas Funcionales para móviles utilizando Appium y lenguaje Gherkin
integrado con Cucumber y Serenity
4.2.1 : Creación y Ejecución de una prueba funcional móvil simple utilizando
Appium y lenguaje Gherkin integrado con Cucumber y Serenity
4.2.2 : Creación y Ejecución de una prueba funcional móvil de regular
complejidad utilizando Appium y lenguaje Gherkin integrado con
Cucumber y Serenity
4.2.3 : Creación y Ejecución de una prueba funcional móvil avanzada utilizando
Appium y lenguaje Gherkin integrado con Cucumber y Serenity
ACTIVIDADES PROPUESTAS
Con Appium, Podemos escribir los tests en el lenguaje que más nos guste: Java, Objective-C,
JavaScript con Node.js, PHP, Python, Ruby, C#, Clojure, o Perl, en todos los casos usando el API
de Selenium WebDriver (Genbeta, s.f.)
Adaptado de Verma, S. (2017, Diciembre 22). Appium – A Good Open Source Mobile App Testing Framework. Trigent.
https://blog.trigent.com/appium-a-good-open-source-mobile-app-testing-framework/
https://github.com/appium/appium-desktop/releases/tag/v1.22.3-4
Appium Inspector:
Appium Inspector es una aplicación que nos permite, de forma rápida, inspeccionar una
aplicación que se está mostrando en un emulador/simulador Android o iOS y que nos permite
ver:
Apps Nativas: Las aplicaciones nativas se escriben usando el SDK del sistema operativo. Entre
las aplicaciones nativas tenemos Snapchat, Facebook, entre otras. Las aplicaciones nativas están
diseñadas para un sistema operativo específico. Esto significa que las aplicaciones de iPhone y
Android se deben crear de forma separada una de otra.
Web Apps: Se accede directamente desde el navegador del teléfono o tablet, y en casi todos los
dispositivos se abrirá casi de la misma manera gracias al uso del Responsive Web Design. Por
ejemplo, algunas aplicaciones como Facebook se crean como aplicaciones nativas y aplicaciones
móviles. Si bien debes descargar aplicaciones nativas, las aplicaciones web no requieren que
instales nada y solo se puede acceder abriendo un navegador de Internet y buscando o
ingresando al sitio web.
Apps Híbridas: Las aplicaciones híbridas se crean principalmente con HTML, CSS y JavaScript,
que es lo mismo que se utiliza para crear aplicaciones web, aunque también se usa código nativo,
por lo que estas aplicaciones se denominan “híbridas”. Para pensarlo de manera más simple,
pueden describirse como aplicaciones web “empaquetadas en un contenedor nativo”.
Android Studio
Luego de instalar Android Studio agregar el SDK de Android a las variables de entorno
ANDROID_HOME:
Es una herramienta mediante línea de comandos que permite a una computadora Windows,
Linux o Mac comunicarse con un dispositivo Android. Permite hacer una gran variedad de
acciones sobre el dispositivo Android, entre las que están enviar ficheros desde el ordenador,
copiar ficheros presentes en el dispositivo Android, depurar aplicaciones, instalar y desinstalar
paquetes y proporciona acceso a una shell de Unix.
Generar un emulador en Android, para ello seleccionar AVD Manager de la parte superior del
proyecto:
Figura 4.10: Vista Project
Aceptar:
Figura 4.14: Aceptar
Esperar la instalación:
Finalizar:
Seleccionar “Finish”:
Iniciar el emulador:
Aparecerá el emulador:
Clic derecho sobre el terminar y seleccionar propiedades, cambiar el tamaño del búfer a 999 y
luego “Aceptar”:
En CMD colocar el comando “adb devices”, luego enter (copiemos el nombre del emulador
“emulator-5554”). Luego colocar el comando “adb logcat”:
Al presionar enter, se va a visualizar todo el log del emulador (no cerrar el CMD):
Luego, copiamos todo el contenido del CMD haciendo clic derecho, Editar, Seleccionar todo:
Luego, hacemos clic derecho sobre el CMD para copiar todo el contenido seleccionado (se
observa que todo lo seleccionado se desmarcará) y pegamos el contenido en un editor de texto:
Vamos a validar con qué parámetros levantamos nuestra aplicación, para ello levantamos el
programa “Appium Server GUI” y hacemos clic en el botón starServer:
En la sección de capabilities agregamos los siguientes valores para validar si con la configuración
obtenida de “LoginActivity” levantamos la aplicación desde Appium Inspector la aplicación:
platformName: Android
deviceName: emulator-5554
appPackage: com.ecommerce.template
appActivity: .activity.LoginActivity
Los valores agregados deben quedar de la siguiente forma, luego clickear en el botón “Start
Sesion”
En la sección de capabilities modificamos los valores para validar si con la configuración obtenida
de “SplashScreenActivity” levantamos la aplicación desde Appium Inspector la aplicación:
platformName: Android
deviceName: emulator-5554
appPackage: com.ecommerce.template
appActivity: .activity.SplashScreenActivity
Los valores agregados deben quedar de la siguiente forma, luego clickear en el botón “Start
Sesion”
Asimismo, también se cargó la aplicación en el Emulador, esto es debido a que Appium Inspector
está enviando peticiones al emulador:
Dentro de Appium Inspector hacemos clic en botón “Refresh” para sincronizar la vista del
Appium Inspector con el emulador, es muy común que Appium Inspector no sincronice la vista
con el emulador, es por ello por lo que ante cada petición que se haga desde Appium Inspector,
siempre hay que ir refrescando para traer la última vista del emulador:
Luego de haber presionado el botón Refresh, se sincroniza la vista del Appium Inspector con la
del emulador.
Ahora vamos a generar un script utilizando el Appium Inspector, para ello hacemos clic en el
botón de grabación:
Una vez iniciada la grabación, interactuamos con el apk pero desde Appium Inspector (no
interactuar con la aplicación desde el emulador), seleccionamos el campo email y donde dice
“Enter Keys to Send” ingresamos un email cualquiera y presionando el botón “Send Keys”:
Ahora ingresamos un password cualquiera, realizando los mismos pasos que se hizo para el
email, es decir primero seleccionamos el campo password, ingresamos un password y
presionamos el botón “Send Keys”, aparecerá el password ingresado y el código generado:
Seleccionamos el botón “Sign In” y hacemos clic en el botón “Tap” (esto origina que se haga clic
sobre el botón), abremos ingresado a la aplicación:
Hacemos clic sobre el título “E-Commerce App Template” y presionamos el botón “Tap”, esto
nos ayudará para capturar el elemento título como código para luego hacer la prueba unitaria:
Copiamos el código generado en Appium Inspector, con el botón “Copy code to clipboard”:
Por último, terminamos la grabación clickeando sobre el botón “Quit Session & Close Inspector”:
Podemos guardar la configuración con el botón “Save As” y luego botón “Save”:
Para integrar el script de Appium con Android Studio, primero se debe realizar una grabación
utilizando Appium Inspector, esta grabación generará un script de prueba en JUnit, este código
luego se importa en Android Studio.
Resumen
1. Appium es una herramienta open-source para la automatización de aplicaciones web
nativas e híbridas en las plataformas móviles iOS y Android.
3. Appium es una herramienta de código abierto, lo que significa que está disponible
gratuitamente. Es fácil de instalar.
4. Una función adicional añadida a Appium. Ahora también admitiría pruebas de aplicaciones
de escritorio para Windows junto con pruebas de aplicaciones móviles.
Recursos
Pueden revisar los siguientes enlaces para ampliar los conceptos vistos en esta unidad:
o https://www.youtube.com/watch?v=0GEvygj7ETc
o https://www.youtube.com/watch?v=tFcmRVWts4Q
o https://www.youtube.com/watch?v=ZFSFuQWB4wk
Implemente las pruebas funcionales móviles con Appium para realizar el logueo de una
aplicación sobre Android.
Tomando como base el script que generamos en la sección “4.1.2. Generación de script con
Appium”, tomamos como base el proyecto generado y agregamos en el archivo “build.gradle”
las siguientes dependencias de Selenium y Appium:
implementation 'org.seleniumhq.selenium:selenium-java:4.4.0'
implementation 'io.appium:java-client:8.2.0'
package pe.edu.cibertec;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import io.appium.java_client.android.AndroidDriver;
@Before
public void setUp() throws MalformedURLException {
DesiredCapabilities desiredCapabilities = new
DesiredCapabilities();
desiredCapabilities.setCapability("platformName", "Android");
desiredCapabilities.setCapability("deviceName", "emulator-
5554");
desiredCapabilities.setCapability("appPackage",
"com.ecommerce.template");
desiredCapabilities.setCapability("appActivity",
".activity.SplashScreenActivity");
@Test
public void login() {
WebDriverWait wait = new WebDriverWait(driver,
Duration.ofSeconds(30));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("/hi
erarchy/android.widget.FrameLayout/android.widget.LinearLayout/android
.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLa
yout/android.widget.LinearLayout/android.widget.LinearLayout[1]/androi
d.widget.LinearLayout[1]/android.widget.FrameLayout/android.widget.Edi
tText")));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("/hi
erarchy/android.widget.FrameLayout/android.widget.LinearLayout/android
.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLa
yout/androidx.drawerlayout.widget.DrawerLayout/android.widget.LinearLa
yout/android.view.ViewGroup/android.widget.TextView")));
WebElement el4 = (WebElement)
driver.findElement(By.xpath("/hierarchy/android.widget.FrameLayout/and
roid.widget.LinearLayout/android.widget.FrameLayout/android.widget.Lin
earLayout/android.widget.FrameLayout/androidx.drawerlayout.widget.Draw
erLayout/android.widget.LinearLayout/android.view.ViewGroup/android.wi
dget.TextView"));
Assert.assertEquals("E-Commerce App Template",el4.getText());
@After
public void tearDown() {
driver.quit();
}
}
Antes de ejecutar la prueba unitaria, estar seguros, que el emulador esté corriendo y que el
servidor de Appium está ejecutándose (no es necesario iniciar el Appium Inspector):
Ejecutamos la prueba unitaria, observaremos que el emulador ejecutará los pasos del login y
terminará de forma exitosa:
Implemente las pruebas funcionales móviles con Appium para realizar el logueo, la selección de
un producto y el proceso de compra de una aplicación sobre Android.
package pe.edu.cibertec;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import io.appium.java_client.android.AndroidDriver;
@Before
public void setUp() throws MalformedURLException {
DesiredCapabilities desiredCapabilities = new
DesiredCapabilities();
desiredCapabilities.setCapability("platformName", "Android");
desiredCapabilities.setCapability("deviceName", "emulator-
5554");
desiredCapabilities.setCapability("appPackage",
"com.ecommerce.template");
desiredCapabilities.setCapability("appActivity",
".activity.SplashScreenActivity");
@Test
public void compra() {
WebDriverWait wait = new WebDriverWait(driver,
Duration.ofSeconds(30));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("/hi
erarchy/android.widget.FrameLayout/android.widget.LinearLayout/android
.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLa
yout/android.widget.LinearLayout/android.widget.LinearLayout[1]/androi
d.widget.LinearLayout[1]/android.widget.FrameLayout/android.widget.Edi
tText")));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("/hi
erarchy/android.widget.FrameLayout/android.widget.LinearLayout/android
.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLa
yout/androidx.drawerlayout.widget.DrawerLayout/android.widget.LinearLa
yout/android.widget.ScrollView/android.widget.LinearLayout/android.wid
get.LinearLayout/android.widget.LinearLayout/androidx.recyclerview.wid
get.RecyclerView/android.widget.LinearLayout[1]/android.widget.LinearL
ayout")));
@After
public void tearDown() {
driver.quit();
}
}
Ejecutamos la prueba unitaria, observaremos que el emulador ejecutará los pasos del proceso
de compra y terminará de forma exitosa:
Implemente las pruebas funcionales móviles con Appium para realizar el logueo, la selección de
un producto y el proceso de compra de una aplicación sobre Android con lenguaje Gherkin.
<groupId>pe.edu.cibertec</groupId>
<artifactId>Tema8Ejercicio3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Tema8Ejercicio3</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>20</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.10.0</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.12.1</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>7.12.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<version>7.12.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>8.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Usuario {
private String usuario;
private String password;
}
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.MalformedURLException;
import java.net.URL;
"Android");
desiredCapabilities.setCapability("deviceName",
"emulator-5554");
desiredCapabilities.setCapability("appPackage",
"com.ecommerce.template");
desiredCapabilities.setCapability("appActivity",
".activity.SplashScreenActivity");
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import pe.edu.cibertec.model.Usuario;
import java.time.Duration;
driver.findElement(By.name("username")).sendKeys(usuario.getUsuar
io());
driver.findElement(By.name("password")).click();
driver.findElement(By.name("password")).sendKeys(usuario.getPassw
ord());
driver.findElement(By.cssSelector(".btn")).click();
}
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath
("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayo
ut/android.widget.FrameLayout/android.widget.LinearLayout/android
.widget.FrameLayout/android.widget.LinearLayout/android.widget.Li
nearLayout[1]/android.widget.LinearLayout[1]/android.widget.Frame
Layout/android.widget.EditText")));
out[1]/android.widget.FrameLayout/android.widget.EditText"));
el1.sendKeys("christophermen@gmail.com");
WebElement el2 = (WebElement)
driver.findElement(By.xpath("/hierarchy/android.widget.FrameLayou
t/android.widget.LinearLayout/android.widget.FrameLayout/android.
widget.LinearLayout/android.widget.FrameLayout/android.widget.Lin
earLayout/android.widget.LinearLayout[1]/android.widget.LinearLay
out[2]/android.widget.FrameLayout/android.widget.EditText"));
el2.sendKeys("1234");
WebElement el3 = (WebElement)
driver.findElement(By.id("com.ecommerce.template:id/bt_sign_in"))
;
el3.click();
WebElement el4 = (WebElement)
driver.findElement(By.xpath("/hierarchy/android.widget.FrameLayou
t/android.widget.LinearLayout/android.widget.FrameLayout/android.
widget.LinearLayout/android.widget.FrameLayout/androidx.drawerlay
out.widget.DrawerLayout/android.widget.LinearLayout/android.widge
t.ScrollView/android.widget.LinearLayout/android.widget.LinearLay
out/android.widget.LinearLayout/androidx.recyclerview.widget.Recy
clerView/android.widget.LinearLayout[1]/android.widget.LinearLayo
ut"));
el4.click();
WebElement el5 = (WebElement)
driver.findElement(By.id("com.ecommerce.template:id/btn_addtocart
"));
el5.click();
WebElement el6 = (WebElement)
driver.findElement(By.id("com.ecommerce.template:id/text_checkout
"));
el6.click();
WebElement el7 = (WebElement)
driver.findElement(By.id("com.ecommerce.template:id/text_save"));
el7.click();
WebElement el8 = (WebElement)
driver.findElement(By.id("com.ecommerce.template:id/text_paynow")
);
el8.click();
}
import io.cucumber.java.After;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.junit.Assert;
import pe.edu.cibertec.appium.BaseAppium;
import pe.edu.cibertec.appium.Compra;
import pe.edu.cibertec.model.Usuario;
import pe.edu.cibertec.util.PropertyLoader;
@Given("soy un usuario")
public void paso1() {
usuario = new
Usuario(loadproperty.loadProperties().getProperty("loginUsuario")
, loadproperty.loadProperties().getProperty("passwordUsuario"));
}
@And("ingreso al app")
public void paso2() {
BaseAppium baseAppium = new BaseAppium();
try {
BaseAppium.setUp();
Compra login = new Compra();
login.login(usuario);
} catch (Exception e) {
System.out.println("Error connecting to Selenium
Service. Error :" + e.getMessage());
}
}
@After
public void close() {
BaseAppium baseAppium = new BaseAppium();
baseAppium.tearDown();
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
package pe.edu.cibertec;
import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;
import static
io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("feature")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value =
"pe.edu.cibertec")
public class CucumberTest {
}
Background:
Given soy un usuario
And ingreso al app
cucumber.plugin=pretty, json:target/cucumber/cucumber.json,
html:target/cucumber.html
loginUsuario=christopher@gmail.com
passwordUsuario=1234
Antes de ejecutar la prueba unitaria, estar seguros, que el emulador esté corriendo y que el
servidor de Appium está ejecutándose (no es necesario iniciar el Appium Inspector). Al ejecutar
la clase CucumberTest, observaremos que el emulador ejecutará los pasos del proceso de
compra y terminará de forma exitosa:
Resumen
1. Appium es un servidor y se ejecuta en segundo plano.
Recursos
Pueden revisar los siguientes enlaces para ampliar los conceptos vistos en esta unidad:
o https://www.youtube.com/watch?v=k4787wlonBc&list=PL9qpFd_9rkKWJH8v0Vk8qIYMhs
OnB6KdZ
o https://www.youtube.com/watch?v=CjxxX0F9jOk
Bibliografía
• Hans, M. (2015). Appium Essentials (1.ª ed.). Editorial Packt Publishing.
• Verma, N. (2017). Mobile Test Automation with Appium: Mobile application testing
made easy (1.ª ed.). Editorial Packt Publishing.
UNIDAD
5
FUNDAMENTOS DE PRUEBAS DE
PERFORMANCE
LOGRO DE LA UNIDAD DE APRENDIZAJE
Al término de la unidad, el alumno realiza pruebas de rendimiento utilizando JMeter.
TEMARIO
5.1 Tema 9 : Introducción a las pruebas de Performance
5.1.1 : Pruebas de performance
5.1.2 : JMeter
ACTIVIDADES PROPUESTAS
Son las pruebas que se realizan para determinar lo rápido que realiza una tarea un
sistema en condiciones particulares de trabajo. También puede servir para validar y
verificar otros atributos de la calidad del sistema, tales como la escalabilidad, fiabilidad
y uso de los recursos. Las pruebas de rendimiento son un subconjunto de la ingeniería
de pruebas, una práctica informática que se esfuerza por mejorar el rendimiento,
englobándose en el diseño y la arquitectura de un sistema, antes incluso del esfuerzo
inicial de la codificación (Verona, 2016).
Las pruebas de rendimiento pueden servir para diferentes propósitos. Pueden áreas,
sistemas demostrar que el sistema cumpla los criterios de rendimiento. Pueden
comparar dos sistemas para encontrar cuál de ellos funciona mejor. O pueden medir
que partes del sistema o de carga de trabajo provocan que el conjunto rinda mal. Para
su diagnóstico, los ingenieros de software utilizan herramientas como pueden ser
monitorizaciones que midan qué partes de un dispositivo o software contribuyen más
al mal rendimiento o para establecer niveles (y umbrales) del mismo que mantenga un
tiempo de respuesta aceptable.
Pruebas de carga
Este es el tipo más sencillo de pruebas de rendimiento. Una prueba de carga se realiza
generalmente para observar el comportamiento de una aplicación bajo una cantidad de
peticiones esperada. Esta carga puede ser el número esperado de usuarios concurrentes
utilizando la aplicación y que realizan un número específico de transacciones durante el
tiempo que dura la carga. Esta prueba puede mostrar los tiempos de respuesta de todas
las transacciones importantes de la aplicación.
Prueba de estrés
Esta prueba normalmente se hace para determinar si la aplicación puede aguantar una
carga esperada continuada. Generalmente esta prueba se realiza para determinar si hay
alguna fuga de memoria en la aplicación.
5.1.2. JMeter
JMeter es un proyecto de Apache que puede ser utilizado como una herramienta de
prueba de carga para analizar y medir el rendimiento de una variedad de servicios, con
énfasis en aplicaciones web.
JMeter puede ser usado como una herramienta de pruebas unitarias para conexiones de
bases de datos con JDBC, FTP, LDAP, Servicios web, JMS, HTTP y conexiones TCP
genéricas. JMeter puede también ser configurado como un monitor, aunque es
comúnmente considerado una solución ad-hoc respecto de soluciones avanzadas de
monitoreo.
Características:
Resumen
1. Es una herramienta de testing creada por Apache.
3. JMeter se suele usar para hacer pruebas de carga, aunque también soporta aserciones
para asegurar que los datos recibidos son correctos y muchas posibilidades a la hora de
generar reportes.
5. Además, en el fichero “.jmx” se puede especificar qué número de usuarios de cada tipo
ejecuta las peticiones contra la aplicación simulando uno o más grupos de usuarios
trabajando contra la aplicación objetivo.
Recursos
Pueden revisar los siguientes enlaces para ampliar los conceptos vistos en esta unidad:
o https://www.youtube.com/watch?v=E2zwM8s7thY
o https://www.youtube.com/watch?v=uguvCxejOJM
o https://www.youtube.com/watch?v=W4HH0TAQFbw
Tomando como base el proyecto “Tema2Sesion2” (visto en el Tema 2), vamos a realizar a este
proyecto las pruebas de stress respectivas, levantamos el proyecto:
Dentro de Jmeter colocamos un nombre al “Test Plan”: “Tema10” y guardamos el proyecto con
el nombre “Tema10.jmx”
Agregar un “Thread Group”, haciendo clic derecho sobre el nombre del “Test Plan”, luego “Add”,
luego “Threads (Users)” y luego “Thread Group”:
Al “Thread Group” renombrarlo con “Tema2Sesion2” luego donde dice “Number of Threads
(users)” colocar el valor de 5:
Clic derecho sobre el “Thread Group” agregado, ir a Add, luego Sampler y seleccionar “HTTP
Request”:
Clic derecho sobre el “HTTP Request” y seleccionamos Add, Listener, “View Result Tree”:
Clic derecho sobre el “HTTP Request” y seleccionamos Add, Listener, “Summary Report”:
Revisamos los resultados obtenidos en “View Result Tree” y “Summary Report”, observamos
que se realizaron 5 peticiones:
Clic derecho sobre el “HTTP Request” y seleccionamos Add, “Post Processors”, “Bean Shell
PostProcessor”:
Dentro del componente “Bean Shell PostProcessor”, agregamos el siguiente código (asegurarse
de que la ruta “E:/Tema10” exista en su PC o modificarla por una ruta existente):
String response=prev.getResponseDataAsString();
Limpiamos los resultados tanto de “View Result Tree” y “Summary Report”, haciendo sobre cada
uno clic derecho “Clear”:
Revisamos los resultados obtenidos en “View Result Tree” y “Summary Report”, observamos
que se realizaron 5 peticiones:
Nuevamente clic derecho sobre el “Thread Group” agregado, ir a Add, luego Sampler y
seleccionar “HTTP Request”, al “HTTP Request” agregado llamarlo “obtenerPersona”, luego
configurarlo de la siguiente forma, estos valores obtenerlos del archivo postman:
Clic derecho sobre el “HTTP Request” y seleccionamos Add, Listener, “View Result Tree” y
“Summary Report”, luego agregamos el componente “CSV Data Set Config”:
Dentro de una ruta válida creamos el archivo “obtenerPersona-input.csv” y dentro que tenga la
primera fila el valor “id” y luego colocar ids que existan en la base de datos:
Dentro del componente “CSV Data Set Config”, configuramos de la siguiente forma,
seleccionando la ruta del archivo “obtenerPersona-input.csv”, en el campo “Variable Names”
indicar “id” (notar que este valor es el mismo del valor de la primera fila del archivo) y en el
campo “Ignore fist line” colocar en True:
Clic derecho sobre el “HTTP Request” y seleccionamos Add, “Post Processors”, “Bean Shell
PostProcessor”, dentro del componente “Bean Shell PostProcessor”, agregamos el siguiente
código (asegurarse de que la ruta “E:/Tema10” exista en su PC o modificarla por una ruta
existente):
String response=prev.getResponseDataAsString();
out.close();
fstream.close();
Figura 5.28: Código “Bean Shell PostProcessor”
Clic sobre el botón “Start” y revisamos los resultados obtenidos en “View Result Tree” y
“Summary Report”, observamos que se realizaron 5 peticiones:
Nuevamente clic derecho sobre el “Thread Group” agregado, ir a Add, luego Sampler y
seleccionar “HTTP Request”, al “HTTP Request” agregado llamarlo “registrarPersona”, luego
configurarlo de la siguiente forma, estos valores obtenerlos del archivo postman, agregando
dentro de la sección “Body Data” el siguiente código:
{
"nombre": "${nombre}",
"apellido": "${apellido}",
"direccion": "${direccion}",
"telefono": "${telefono}",
"distrito":{
"id": ${distritoid},
"nombre": "${distritonombre}"
}
}
Clic derecho sobre el “HTTP Request” y seleccionamos Add, Listener, “View Result Tree” y
“Summary Report”, luego agregamos el componente “CSV Data Set Config”, dentro de una ruta
válida creamos el archivo “registrarPersona-input.csv” con los siguientes valores (los valores de
la primera fila coinciden con los de la sección “Body Data”):
nombre,apellido,direccion,telefono,distritoid,distritonombre
maria,telles,mi direccion,987654321,1,Lima
Dentro del componente “CSV Data Set Config”, configuramos de la siguiente forma,
seleccionando la ruta del archivo “registrarPersona-input.csv”, en el campo “Variable Names”
pegar los valores de la primera fila indicada en el archivo csv y en el campo “Ignore fist line”
colocar en True:
Clic derecho sobre el “HTTP Request” y seleccionamos Add, “Post Processors”, “Bean Shell
PostProcessor”, dentro del componente “Bean Shell PostProcessor”, agregamos el siguiente
código (asegurarse de que la ruta “E:/Tema10” exista en su PC o modificarla por una ruta
existente):
String response=prev.getResponseDataAsString();
Clic derecho sobre el “HTTP Request” y seleccionamos Add, “Config Element”, “HTTP Header
Manager”:
Figura 5.37: Componente “HTTP Header Manager”
Clic sobre el botón “Start” y revisamos los resultados obtenidos en “View Result Tree” y
“Summary Report”, observamos que se realizaron 5 peticiones:
Nuevamente clic derecho sobre el “Thread Group” agregado, ir a Add, luego Sampler y
seleccionar “HTTP Request”, al “HTTP Request” agregado llamarlo “modificarPersona”, luego
configurarlo de la siguiente forma, estos valores obtenerlos del archivo postman, agregando
dentro de la sección “Body Data” el siguiente código:
{
"id": "${id}",
"nombre": "${nombre}",
"apellido": "${apellido}",
"direccion": "${direccion}",
"telefono": "${telefono}",
"distrito":{
"id": ${distritoid},
"nombre": "${distritonombre}"
}
}
Clic derecho sobre el “HTTP Request” y seleccionamos Add, Listener, “View Result Tree” y
“Summary Report”, luego agregamos el componente “CSV Data Set Config”, dentro de una ruta
válida creamos el archivo “modificarPersona-input.csv” con los siguientes valores (los valores de
la primera fila coinciden con los de la sección “Body Data”):
id,nombre,apellido,direccion,telefono,distritoid,distritonombre
2452,julio,telles,mi direccion,987654321,1,Lima
Dentro del componente “CSV Data Set Config”, configuramos de la siguiente forma,
seleccionando la ruta del archivo “modificarPersona-input.csv”, en el campo “Variable Names”
pegar los valores de la primera fila indicada en el archivo csv y en el campo “Ignore fist line”
colocar en True:
Clic derecho sobre el “HTTP Request” y seleccionamos Add, “Post Processors”, “Bean Shell
PostProcessor”, dentro del componente “Bean Shell PostProcessor”, agregamos el siguiente
código (asegurarse de que la ruta “E:/Tema10” exista en su PC o modificarla por una ruta
existente):
String response=prev.getResponseDataAsString();
Clic derecho sobre el “HTTP Request” y seleccionamos Add, “Config Element”, “HTTP Header
Manager”, luego clic sobre el botón “Add” y agregamos la variable “Content-Type” con el valor
“application/json”:
Clic sobre el botón “Start” y revisamos los resultados obtenidos en “View Result Tree” y
“Summary Report”, observamos que se realizaron 5 peticiones:
Nuevamente clic derecho sobre el “Thread Group” agregado, ir a Add, luego Sampler y
seleccionar “HTTP Request”, al “HTTP Request” agregado llamarlo “eliminarPersona”, luego
configurarlo de la siguiente forma, estos valores obtenerlos del archivo postman:
Figura 5.48: Configurar HTTP Request
Clic derecho sobre el “HTTP Request” y seleccionamos Add, Listener, “View Result Tree” y
“Summary Report”, luego agregamos el componente “CSV Data Set Config”, dentro de una ruta
válida creamos el archivo “eliminarPersona-input.csv” con los siguientes valores (ids con valores
que existan en la base de datos):
Dentro del componente “CSV Data Set Config”, configuramos de la siguiente forma,
seleccionando la ruta del archivo “eliminarPersona-input.csv”, en el campo “Variable Names”
pegar los valores de la primera fila indicada en el archivo csv y en el campo “Ignore fist line”
colocar en True:
Clic derecho sobre el “HTTP Request” y seleccionamos Add, “Post Processors”, “Bean Shell
PostProcessor”, dentro del componente “Bean Shell PostProcessor”, agregamos el siguiente
código (asegurarse de que la ruta “E:/Tema10” exista en su PC o modificarla por una ruta
existente):
String response=prev.getResponseDataAsString();
Clic derecho sobre el “HTTP Request” y seleccionamos Add, “Config Element”, “HTTP Header
Manager”, luego clic sobre el botón “Add” y agregamos la variable “Content-Type” con el valor
“application/json”:
Clic sobre el botón “Start” y revisamos los resultados obtenidos en “View Result Tree” y
“Summary Report”, observamos que se realizaron 5 peticiones:
Analizar con su docente los resultados obtenidos en los archivos “View Result Tree” y “Summary
Report”, asimismo revisar los resultados obtenidos en los archivos “output.csv”
Resumen
1. JMeter fue diseñado para realizar pruebas de carga en servidores o aplicativos Web por
medio del protocolo HTTP.
Recursos
Pueden revisar los siguientes enlaces para ampliar los conceptos vistos en esta unidad:
o https://www.youtube.com/watch?v=53BCAlhIuhQ
o https://www.youtube.com/watch?v=T8mWjZmVQPU
o https://www.youtube.com/watch?v=x8eSbRUu00k
Bibliografía
• Adzic, G. (2009). Mockito in six easy examples. https://gojko.net/2009/10/23/mockito-in-
six-easy-examples/
• Erinle, B. (2017). Performance Testing with JMeter 3 - Third Edition: Enhance the
performance of your web application (3.ª ed.). Editorial Packt Publishing.
• Ferguson, J. (2014). BDD in Action: Behavior-driven development for the whole software
lifecycle (1.ª ed.). Editorial Manning Publications.
• Garcia, B. (2022). Hands-On Selenium WebDriver with Java: A Deep Dive into the
Development of End-to-End Tests (1.ª ed.). Editorial O'Reilly Media.
• Genbeta. https://www.genbeta.com/desarrollo/automatizacion-de-pruebas-web-moviles-
appium-nightwatch-js
• Gundecha, U. (2015). Selenium Testing Tools Cookbook (2.ª ed.). Editorial Packt Publishing.
• ISO 9001:2015 (2023). Testing con Cucumber: Cómo afianzar la fiabilidad en los desarrollos.
https://www.chakray.com/es/testing-cucumber-afianzar-fiabilidad-desarrollos/
• Matam, S. (2017). Pro Apache JMeter: Web Application Performance Testing (1.ª ed.).
Editorial Apress.
• Stack Exchange Inc (2023). What's the best mock framework for Java? [closed].
https://stackoverflow.com/questions/22697/whats-the-best-mock-framework-for-java
• Verma, N. (2017). Mobile Test Automation with Appium: Mobile application testing made
easy (1.ª ed.). Editorial Packt Publishing.
• Wynne, M., & Hellesoy, A. (2012). The Cucumber Book: Behaviour-Driven Development for
Testers and Developers (1.ª ed.). Pragmatic Bookshelf.