Está en la página 1de 261

Pruebas de Software

PRUEBAS DE SOFTWARE 2

Curso Pruebas de Software (SP2424)


Formato Manual de curso
Autor Institucional Cibertec
Páginas 261 p.
Elaborador Ilizarbe Monteagudo, Henry
Christopher
Revisor de Contenidos Morales Flores, Gustavo Elías

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 3

Índice
Presentación 5
Red de contenidos 6

UNIDAD DE APRENDIZAJE 1: FUNDAMENTOS DE TDD

1.1 Tema 1 : Introducción a TDD 8


1.1.1 : Pruebas Unitarias 15
1.1.2 : TDD 26
1.1.3 : Pruebas de Integración 27
1.1.4 : Pruebas Funcionales 28
1.1.5 : Pruebas de Sistema 28
1.1.6 : Pruebas de Aceptación 29

1.2 Tema 2 : Pruebas Unitarias con el framework JUnit y Mockito 31


1.2.1 : Creación y Ejecución de una prueba unitaria simple con JUnit en 31
IntelliJ
1.2.2 : Creación y Ejecución de una prueba unitaria de regular complejidad 53
con JUnit en IntelliJ
1.2.3 : Creación y Ejecución de una prueba unitaria avanzada con JUnit y 59
Mockito en IntelliJ

UNIDAD DE APRENDIZAJE 2: FUNDAMENTOS DE BDD

2.1 Tema 3 : Introducción a BDD 81


2.1.1 : BDD 81
2.1.2 : Lenguaje Gherkin 82
2.1.3 : Cucumber 82
2.1.4 : Rest Assured 83
2.1.5 : Serenity 84

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

UNIDAD DE APRENDIZAJE 3: FUNDAMENTOS DE PRUEBAS FUNCIONALES WEB

3.1 Tema 5 : Pruebas funcionales con Selenium 120


3.1.1 : Selenium IDE 120
3.1.2 : Configuración de Selenium 121
3.1.3 : Integrar script de Selenium con IntelliJ 134

3.2 Tema 6 : Tema 6: Pruebas Funcionales web utilizando Selenium y lenguaje 135
Gherkin integrado con Cucumber y Serenity

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 4

3.2.1 : Creación y Ejecución de una prueba funcional web simple 135


utilizando Selenium y lenguaje Gherkin integrado con Cucumber y
Serenity
3.2.2 : Creación y Ejecución de una prueba funcional web de regular 143
complejidad utilizando Selenium y lenguaje Gherkin integrado con
Cucumber y Serenity
3.2.3 : Creación y Ejecución de una prueba funcional web avanzada 157
utilizando Selenium y lenguaje Gherkin integrado con Cucumber y
Serenity

UNIDAD DE APRENDIZAJE 4: FUNDAMENTOS DE PRUEBAS FUNCIONALES PARA


MÓVILES

4.1 Tema 7 : Pruebas funcionales con Appium 177


4.1.1 : Appium 177
4.1.2 : Generación de script con Appium 181
4.1.3 : Integrar script de Appium con Android Studio 210

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

UNIDAD DE APRENDIZAJE 5: FUNDAMENTOS DE PRUEBAS DE PERFORMANCE

5.1 Tema 9 : Introducción a las pruebas de Performance 230


5.1.1 : Pruebas de performance 230
5.1.2 : JMeter 231

5.2 Tema 10 : Pruebas de rendimiento con JMeter 235


5.2.1 : Crear y ejecutar pruebas de rendimiento con JMeter 235
5.2.2 : Análisis de resultados 258

Bibliografía 260

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 5

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.

El curso es teórico práctico: consiste en sesiones teóricas acompañadas con aplicaciones


prácticas. En primer lugar, se explica la importancia de la verificación y validación de software
para el control de calidad del producto de software, luego se presenta una introducción al Test
Driven Development utilizando JUnit y Mockito. Continúa con una introducción al Behavior
Driven Development utilizando lenguaje Gherkin integrado con Cucumber, Serenity y Rest
Assured. Luego se aborda la automatización de prueba funcionales web con Selenium y pruebas
funcionales en móviles con Appium. Por último, se concluye con la aplicación de JMeter para la
ejecución de pruebas de rendimiento.

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 6

Red de contenidos

Pruebas de Software

Unidad 1 Unidad 2 Unidad 3 Unidad 4 Unidad 5

Fundamentos de Fundamentos de Fundamentos de


Fundamentos de TDD Fundamentos de BDD Pruebas Funcionales Pruebas Funcionales Pruebas de
Web para Móviles Performance

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 7

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

1.2 Tema 2 : Pruebas Unitarias con el framework JUnit y Mockito


1.2.1 : Creación y Ejecución de una prueba unitaria simple con JUnit en IntelliJ
1.2.2 : Creación y Ejecución de una prueba unitaria de regular complejidad con
JUnit en IntelliJ
1.2.3 : Creación y Ejecución de una prueba unitaria avanzada con JUnit y Mockito
en IntelliJ

ACTIVIDADES PROPUESTAS

• Los alumnos interiorizan cómo identificar pruebas unitarias en una aplicación.


• Los alumnos elaboran pruebas unitarias con JUnit y Mockito.
• Los alumnos aplican TDD en la elaboración de pruebas unitarias.

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 8

1.1. INTRODUCCIÓN A TDD


Las pruebas de software intentan demostrar que un programa hace lo que se intenta que haga,
así como descubrir defectos en el programa antes de usarlo. Al probar el software, se ejecuta
un programa con datos artificiales. Hay que verificar los resultados de la prueba que se opera
para buscar errores, anomalías o información de atributos no funcionales del programa.

El proceso de prueba tiene dos metas distintas:

1) Demostrar al desarrollador y al cliente que el software cumple con los requerimientos.


Para el software personalizado, esto significa que en el documento de requerimientos
debe haber, por lo menos, una prueba por cada requerimiento.

2) Encontrar situaciones donde el comportamiento del software sea incorrecto, indeseable


o no esté de acuerdo con su especificación. Tales situaciones son consecuencia de
defectos del software. La prueba de defectos tiene la finalidad de erradicar el
comportamiento indeseable del sistema, como caídas del sistema, interacciones
indeseadas con otros sistemas, cálculos incorrectos y corrupción de datos.

La primera meta conduce a la prueba de validación; en ella, se espera que el sistema se


desempeñe de manera correcta mediante un conjunto dado de casos de prueba, que refleje
el uso previsto del sistema. La segunda meta se orienta a pruebas de defectos, donde los
casos de prueba se diseñan para presentar los defectos. Los casos de prueba en las pruebas
de defecto pueden ser deliberadamente confusos y no necesitan expresar cómo se usa
normalmente el sistema. Desde luego, no hay frontera definida entre estos dos enfoques de
pruebas. Durante las pruebas de validación, usted descubrirá defectos en el sistema; en
tanto que durante las pruebas de defecto algunas de las pruebas demostrarán que el
programa cumple con sus requerimientos. (Sommerville, 2011, p.206)

La prueba de software es el proceso de evaluar y verificar que una aplicación de software


hace lo que se supone que debe hacer. Los beneficios de las pruebas incluyen la prevención
de errores, la reducción de los costos de desarrollo y la mejora del rendimiento.
(https://www.ibm.com/es-es/topics/software-testing)

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):

 Verificación (orientado al proceso): Asegurar que los productos seleccionados cumplen


los requisitos de las actividades precedentes. ¿Se está construyendo el producto de la
forma correcta?
 Validación (orientado al producto): Demostrar que un producto o componente de el
mismo satisface los requerimientos del cliente. ¿Se está construyendo el producto
correcto?

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.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 9

La finalidad de la verificación es comprobar que el software cumpla con su funcionalidad y con


los requerimientos no funcionales establecidos. Sin embargo, la validación es un proceso más
general. La meta de la validación es garantizar que el software cumpla con las expectativas del
cliente. Va más allá del simple hecho de comprobar la conformidad con la especificación, para
demostrar que el software hace lo que el cliente espera que haga. La validación es esencial pues,
las especificaciones de requerimientos no siempre reflejan los deseos o las necesidades reales
de los clientes y usuarios del sistema. (Sommerville, 2011, p.206)

Los objetivos de las pruebas de software son:

 Presentar información sobre la calidad del producto.


 Encontrar defectos o bugs.
 Defectos arquitectónicos y de diseño.
 Funcionalidad no válida o incorrecta.
 Vulnerabilidades de seguridad.
 Problemas de escalabilidad

El objetivo final de los procesos de verificación y validación es establecer confianza de que el


sistema de software es “adecuado”. Esto significa que el sistema tiene que ser lo bastante eficaz
para su uso esperado. El nivel de confianza adquirido depende tanto del propósito del sistema
y las expectativas de los usuarios del sistema, como del entorno del mercado actual para el
sistema. (Sommerville, 2011, p.206)

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

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 10

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)

Figura 1.1: Modelo del proceso de pruebas de software

Adaptado de Sommerville I., 2011. Ingeniería de Software (9.ª ed.). Editorial Addison-Wesley.

En la práctica, el proceso de prueba por lo general requiere una combinación de pruebas


manuales y automatizadas. En las primeras pruebas manuales, un examinador opera el
programa con algunos datos de prueba y compara los resultados con sus expectativas.
Anota y reporta las discrepancias con los desarrolladores del programa. En las pruebas
automatizadas, éstas se codifican en un programa que opera cada vez que se prueba el
sistema en desarrollo. Comúnmente esto es más rápido que las pruebas manuales, sobre
todo cuando incluye pruebas de regresión, es decir, aquellas que implican volver a correr
pruebas anteriores para comprobar que los cambios al programa no introdujeron nuevos
bugs. (Sommerville, 2011, p.206)

La automatización de pruebas es el proceso de controlar la ejecución de pruebas y realizar la


comparación entre los resultados obtenidos y los resultados esperados. Al automatizar pruebas
de software se busca simplificar el trabajo dispendioso, repetitivo o complejo, haciéndolo
efectivo y más productivo. De esta manera, es posible ahorrar energía, tiempo y costos
asociados. A su vez, liberar de tiempo valioso a los testers para que se concentren en otras
tareas.

El uso de pruebas automatizadas aumentó de manera considerable durante los últimos


años. Sin embargo, las pruebas nunca pueden ser automatizadas por completo, ya que
esta clase de pruebas sólo comprueban que un programa haga lo que supone que tiene
que hacer. Es prácticamente imposible usar pruebas automatizadas para probar
sistemas que dependan de cómo se ven las cosas (por ejemplo, una interfaz gráfica de
usuario) o probar que un programa no presenta efectos colaterales indeseados.
(Sommerville, 2011, p.206)

El costo de detectar y corregir defectos en el software aumenta exponencialmente a medida


que se avanza dentro del ciclo de vida del desarrollo del software (figura 1.2).

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 11

Figura 1.2: Costo de los Defectos

Adaptado de Torcivia, M. (2020, Noviembre 5). SHIFT-LEFT TESTING. Medium. https://medium.com/globant/shift-left-testing-


c838521617b0

Esto se debe a que el impacto y la cantidad de retrabajo es mayor cuando se encuentra o se


corrige en fases posteriores y no en las mismas. Por ejemplo, si un defecto de diseño se extiende
a la fase de construcción y se detecta en la fase de prueba, el costo de reparación es alto, ya que
ya pasó por el proceso de construcción, diseño y prueba, entonces se debe invertir un esfuerzo
importante para repararlo. En cambio, si el defecto se detecta y corrige en la misma fase, este
impacto se reduce.

En el modelo de calidad tradicional se tiene las siguientes características (figura 1.3):

 El equipo de QA suele estar menos involucrado en las primeras fases.


 Muchos requerimientos y defectos de diseño no se descubren y corrigen hasta que se
ha desperdiciado un esfuerzo significativo en su implementación.
 Menos tiempo para corregir defectos.
 Correcciones de última hora, poniendo en peligro el release.
 El tiempo de prueba se reduce y, por lo general, se convierte en un cuello de botella.
 Pruebas como fase al final del ciclo de desarrollo.

Figura 1.3: Modelo de calidad tradicional

Adaptado de Torcivia, M. (2020, Noviembre 5). SHIFT-LEFT TESTING. Medium. https://medium.com/globant/shift-left-testing-


c838521617b0

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 12

Entonces, comprender el costo de los defectos y la carga de tener actividades de aseguramiento


de la calidad hacia las últimas etapas del ciclo de vida del desarrollo del software, impulsa a las
organizaciones a buscar diferentes enfoques y estrategias para abordar estas áreas de mejora.

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).

Figura 1.4: Shift Left Testing

Adaptado de Torcivia, M. (2020, Noviembre 5). SHIFT-LEFT TESTING. Medium. https://medium.com/globant/shift-left-testing-


c838521617b0

Las prácticas de Shift-Left consisten en integrar sus pruebas en su proceso de desarrollo de


software y, por lo tanto, descubrir defectos antes, cuando es más fácil y menos costoso
repararlos.

¿Por qué Shift-Left?

 Ayuda a identificar de forma proactiva defectos al inicio del desarrollo.


 Aumente la eficiencia / disminuye los costos.
 Ayuda a reducir los defectos en producción.

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).

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 13

Figura 1.5: Shift Left Testing

Adaptado de Torcivia, M. (2020, Noviembre 5). SHIFT-LEFT TESTING. Medium. https://medium.com/globant/shift-left-testing-


c838521617b0

La clave del éxito en la implementación de un enfoque Shift-Left es involucrar a los analistas de


calidad al comienzo del ciclo de vida de desarrollo de software, lo que a su vez les permitiría
comprender los requisitos, el diseño del software, la arquitectura, la codificación y su
funcionalidad, hacer preguntas difíciles a clientes, analistas comerciales y desarrolladores,
busque aclaraciones y brinde comentarios siempre que sea posible para apoyar al equipo.

Esta participación y comprensión llevarán al analista de calidad a obtener un conocimiento


completo sobre el producto, pensar en varios escenarios, diseñar escenarios en tiempo real
basados en el comportamiento del software que ayudarían al equipo a identificar los defectos
incluso antes de que se realice la codificación.

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):

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 14

Figura 1.6: Shift Left Testing

Adaptado de Torcivia, M. (2020, Noviembre 5). SHIFT-LEFT TESTING. Medium. https://medium.com/globant/shift-left-testing-


c838521617b0

Beneficios de Shift-Left:

 Detección temprana: encontrar defectos temprano, facilita su solución antes de que se


conviertan en un problema significativo en producción.
 Ahorro de costos: mayor eficiencia en los equipos de desarrollo.
 Confiabilidad en las pruebas: mejor comprensión del negocio, por lo que se diseñan
pruebas más completas gracias a la participación de QA desde el principio.
 Potenciar el trabajo en equipo: la calidad es responsabilidad de todos.
 Acelerar el Delivery: permite realizar las pruebas lo antes posible en el proceso de
desarrollo.

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.

La implementación de las pruebas Shift-Left tendrá un profundo impacto en la cultura de una


organización, para disfrutar de sus beneficios, cada organización debe:

 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.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 15

Pirámide de Automatización de Pruebas

Cuando la mayoría de nuestros esfuerzos se centran en la automatización a nivel de la interfaz


de usuario, el objetivo es encontrar bugs; mientras que, con la pirámide ágil (pirámide de
automatización de pruebas de Mike Cohn), la idea es prevenirlos.

Cuando en un proyecto se apuesta por automatizar pruebas de software, debería idealmente


tener una base sólida, comenzando con los casos de prueba unitarios y evitando tantos errores
como sea posible.

La automatización debe contar con un feedback inmediato y continuar sucesivamente a las


diferentes capas. De esta forma, las pruebas manuales y exploratorias son más valiosas a nivel
de la interfaz de usuario, para después centrarse en aquellas pruebas que no son posibles de
automatizar.

Este proceso lo explica detalladamente la pirámide de automatización de pruebas de Mike Cohn


(figura 1.7):

Figura 1.7: Pirámide de Automatización de Pruebas

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/

A la izquierda en el antipatrón del cono de helado, vemos cómo se realiza comúnmente la


automatización y a la derecha la forma ideal de las pruebas automatizadas, donde las pruebas
unitarias tienen una mayor relevancia en la pirámide de Cohn.

1.1.1. Pruebas Unitarias

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.

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 16

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.

Se trata del primer nivel de pruebas que debe superar el código.

1.1.1.1. Características de las Pruebas Unitarias

Una prueba unitaria posee cuatro características particulares que debe guardar para
considerarse “unitario”. Estas características son:

1) Atómico: Prueba una parte mínima de código.


Dicho de manera simple, cada prueba unitaria debe probar una -y solo una- “acción”
realizada por un método (mínima cantidad de funcionalidad posible).
Por ejemplo, para un método que retorna el neto de un monto bruto más el IGV
correspondiente, deberá haber un test que verifique recibir en forma correcta el
importe bruto, otro que verifique el cálculo del IGV sobre un importe bruto y finalmente,
un tercer test unitario que verifique el cálculo de un importe bruto más su IGV
correspondiente.

2) Independiente: Cada prueba unitaria DEBE ser independiente de otro.


No puede ser parte de una secuencia de tests que se deba ejecutar en un determinado
orden. Debe funcionar siempre igual independientemente de que se ejecuten otros
tests o no.
Por ejemplo, siguiendo el caso anterior, el test que verifique la suma de un importe
bruto más su IGV correspondiente, no debe depender del test que verifica el cálculo del
IGV.

3) Inocuo: Significa que no altera el estado del sistema.


Un test unitario DEBE poder correrse sin alterar ningún elemento del sistema, es decir,
que no debe, por ejemplo, agregar, editar o eliminar registros de una base de datos.

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.

Los test unitarios se realizan, en cualquier lenguaje de programación, mediante herramientas -


Frameworks - con un formato determinado, conocido como xUnit. La letra x es tan sólo un
prefijo a modo de comodín para referirnos de manera genérica a los numerosos frameworks
basados en el original SUnit. SUnit fue creado por Kent Beck para la plataforma SmallTalk y se
ha portado a una gran variedad de lenguajes y plataformas como Java (JUnit), .Net (NUnit),
Python (PyUnit), Ruby (RubyUnit), Perl (PerlUnit), C++ (CppUnit), etc. Si aprendemos a trabajar
con NUnit o JUnit sabremos hacerlo con cualquier otro framework tipo xUnit porque la filosofía
es siempre la misma.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 17

Exceptuando el caso de Shell Scripting, los frameworks xUnit, utilizan el paradigma de


programación orientada a objetos (OOP) tanto en su anatomía de desarrollo como para su
implementación (creación de test unitarios).

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.

El acto consiste en hacer la llamada al código que queremos probar y la afirmación o


afirmaciones se hacen sobre el resultado de la ejecución, bien mediante validación del estado o
bien mediante validación de la interacción. Se afirma que nuestras expectativas sobre el
resultado se cumplen. Si no se cumplen el framework marcará en rojo cada falsa expectativa.

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.

1.1.1.2. Pruebas Unitarias con el framework JUnit

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.

Tabla 1.1: Métodos assert()

Método assert de JUnit Qué comprueba


assertTrue(expresión) comprueba que la expresión se
evalúe a true
assertFalse(expresión) comprueba que la expresión se evalúe
a false
assertEquals(valor esperado, valor real) comprueba que el valor esperado
sea igual al valor real
assertNull(objeto) comprueba que el objeto sea null
assertNotNull(objeto) comprueba que el objeto no sea null

comprueba que el
assertSame(objeto_esperado, objeto_real) objeto_esperado y el objeto_real sean
el mismo objeto

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 18

comprueba que el objeto_esperado no


assertNotSame(objeto_esperado, objeto_real) sea el mismo
que el objeto_real
assertArrayEquals Compara dos arreglos y afirma distintas
propiedades de el mismo

assertThat Compara un tipo de dato u objeto. A


diferencia de los assertions normales
este trabaja con Matcher. Matcher es
una adición externa al framework JUnit.
fail() hace que el test termine con fallo
Nota: Elaboración propia

1.1.1.3. Pruebas Unitarias con el framework JUnit en IntelliJ IDEA

Descargamos el IntelliJ IDEA de la siguiente ruta (versión Community Edition):


https://www.jetbrains.com/idea/download/#section=windows

Figura 1.8: Descargar IntelliJ IDEA

Nota: Elaboración propia

Se procede con la instalación del IntelliJ IDEA edición Community. Luego, generamos un nuevo
proyecto Java con su respectiva clase llamada “Suma”:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 19

Figura 1.9: Generar un nuevo proyecto Java

Nota: Elaboración propia

Figura 1.10: Generar una nueva clase Suma

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 20

Figura 1.11: Clase Suma

Nota: Elaboración propia

Código de la clase Suma:

package pe.edu.cibertec;

public class Suma {


private int num1;
private int num2;

public Suma(int n1, int n2) {


num1 = n1;
num2 = n2;
}

public int sumar() {


int resultado = num1 + num2;
return resultado;
}
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 21

Figura 1.12: Código de la clase SumaTest

Nota: Elaboración propia

Generar clase SumaTest, para ello seleccionar la clase Suma, clic derecho opción “Generate”,
opción “Test”, colocar Class Name “SumaTest”:

Figura 1.13: Opción Generate

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 22

Figura 1.14: Generar Test

Nota: Elaboración propia

Figura 1.15: Generar SumaTest

Nota: Elaboración propia

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”:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 23

Figura 1.16: More Options

Nota: Elaboración propia

Figura 1.17: Add JUnit

Nota: Elaboración propia

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:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 24

Figura 1.18: Recargar cambios en Maven

Nota: Elaboración propia

Agregar el siguiente código de la clase SumaTest:

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);
}
}

Aparecerá un error en la anotación “@Test”, posicionarse sobre la palabra y seleccionar la


opción “Import Class” para solucionar el problema:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 25

Figura 1.19: Import Class

Nota: Elaboración propia

Figura 1.20: SumaTest solucionado

Nota: Elaboración propia

Ejecutar el Test con clic derecho, Run SumaTest:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 26

Figura 1.21: Ejecutar SumaTest

Nota: Elaboración propia

Figura 1.22: Resultado ejecución SumaTest

Nota: Elaboración propia

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:

 La implementación de las funciones justas que el cliente necesita y no más.


 La minimización del número de defectos que llegan al software en fase de producción.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 27

 La producción de software modular, altamente reutilizable y preparado para el cambio.

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)

Los pasos para aplicar TDD son los siguientes:

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

1.1.3. Pruebas de Integración

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.

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 28

1.1.4. Pruebas Funcionales

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).

1.1.5. Pruebas de Sistema

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

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 29

fuente. Luego, cuando ya hubiesen tests unitarios para los nuevos cambios introducidos, se
podrían ir desechando tests de sistema.

1.1.6. Pruebas de Aceptación

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.

Las pruebas de aceptación pertenecen a las últimas etapas previas a la liberación de la


aplicación.

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 30

Resumen
Los beneficios de la automatización de pruebas son los siguientes:

1. Eliminar los errores cuando se realizan pruebas manuales.

2. Reducción de tiempo y costo.

3. Incrementa la productividad de los equipos de trabajo.

4. Garantizar que los cambios de código sean de calidad.

5. Reduce el tiempo y esfuerzo requerido en pruebas de regresión.

6. Incrementa la cobertura de pruebas de software.

7. Minimiza los errores en la ejecución de pruebas.

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

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 31

1.2. PRUEBAS UNITARIAS CON EL FRAMEWORK JUNIT Y MOCKITO


1.2.1. Creación y Ejecución de una prueba unitaria simple con JUnit en IntelliJ

Ingresamos al Spring Initializr (https://start.spring.io/) y generamos un nuevo proyecto


seleccionando Maven y Java 20. En Group agregamos “pe.edu.cibertec” y en Artifact
“Tema2Sesion1”:

Figura 1.23: Generar proyecto en Spring Initializr

Nota: Elaboración propia

Seleccionamos el botón “ADD DEPENDENCIES” y agregamos Spring Web:

Figura 1.24: Generar proyecto en Spring Initializr

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 32

Figura 1.25: Generar proyecto en Spring Initializr

Nota: Elaboración propia

Luego clickeamos en el botón “GENERATE”, el proyecto se descargará en formato zip:

Figura 1.26: Generar proyecto en Spring Initializr

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 33

Descomprimimos el proyecto en una carpeta e importamos el proyecto:

Figura 1.27: Importar proyecto

Nota: Elaboración propia

Figura 1.28: Importar proyecto

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 34

Figura 1.29: Importar proyecto

Nota: Elaboración propia

Generar una nueva clase llamada “Tema2Sesion1ApplicationTest” y copiar el código del


proyecto que el profesor entregará en clase:

Figura 1.30: Copiar código

Nota: Elaboración propia

Código de la clase “Tema2Sesion1ApplicationTest”:

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;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class Tema2Sesion1ApplicationTest {

@Test
void contextLoads() {
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 35

@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);
}

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 36

@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);

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 37

@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

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 38

public void ejemploAssertNotSame2() {


assertNotSame("Hola","HolA");
}

@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

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 39

Figura 1.32: Resultados obtenidos

Nota: Elaboración propia

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.

Para ello creamos un proyecto “Tema2Sesion1b” (ver página 8, figura 1.9)

Paso 1: Escribimos un test


package pe.edu.cibertec;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class YearUtilitiesTest {


@Test
public void test1() {
YearUtilities instance = new YearUtilities();
assertTrue(instance.isLeap(4));//es bisiesto

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 40

}
}

Paso 2: Correr todos los tests


Nos da error pues la clase YearUtilities no existe. Implementamos lo mínimo necesario:
package pe.edu.cibertec;

public class YearUtilities {


public boolean isLeap(int year){
return true;
}
}

Paso 3: ¿Se puede mejorar el código?


Si.
¿Realmente necesitamos una instancia de YearUtilities?
package pe.edu.cibertec;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class YearUtilitiesTest {


@Test
public void test1() {
assertTrue(YearUtilities.isLeap(4));//es bisiesto
}
}

Decisión de diseño: Método estático


package pe.edu.cibertec;

public class YearUtilities {


public static boolean isLeap(int year){
return true;
}
}

Paso 1: Escribimos un test


@Test
public void test2() {
assertFalse(YearUtilities.isLeap(6));//no es bisiesto
}

Paso 2: Correr todos los tests


Nos da error pues el método isLeap devuelve true cuando debería ser false. Implementamos lo
mínimo necesario:
public class YearUtilities {
public static boolean isLeap(int year){
if(year%4 == 0){
return true;
}else{
return false;
}
}
}

Paso 3: ¿Se puede mejorar el código?


No. Regresamos al Paso 1

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 41

Paso 1: Escribimos un test


@Test
public void test3() {
assertFalse(YearUtilities.isLeap(100));//no es bisiesto
}

Paso 2: Correr todos los tests


Nos da error pues el método isLeap devuelve true cuando debería ser false. Implementamos lo
mínimo necesario:
public class YearUtilities {
public static boolean isLeap(int year){
if(year%4 == 0) {
if (year % 100 == 0) {
return false;
} else {
return true;
}
}else{
return false;
}
}
}

Paso 3: ¿Se puede mejorar el código?


No. Regresamos al Paso 1

Paso 1: Escribimos un test


@Test
public void test4() {
assertTrue(YearUtilities.isLeap(400));//es bisiesto
}

Paso 2: Correr todos los tests


Nos da error pues el método isLeap devuelve false cuando debería ser true. Implementamos lo
mínimo necesario:
public class YearUtilities {
public static boolean isLeap(int year){
if(year%4 == 0){
if(year%100 ==0){
if(year%400 ==0){
return true;
}else{
return false;
}
}else{
return true;
}
}else{
return false;
}
}
}

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;

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 42

import static org.junit.jupiter.api.Assertions.*;

public class YearUtilitiesTest {


@Test
public void test1() {
assertTrue(YearUtilities.isLeap(4));//es bisiesto
}
@Test
public void test2() {
assertFalse(YearUtilities.isLeap(6));//no es bisiesto
}
@Test
public void test3() {
assertFalse(YearUtilities.isLeap(100));//no es bisiesto
}
@Test
public void test4() {
assertTrue(YearUtilities.isLeap(400));//es bisiesto
}
@Test
public void test5() {
assertFalse(YearUtilities.isLeap(2015));//no es bisiesto
}
@Test
public void test6() {
assertTrue(YearUtilities.isLeap(2016));//es bisiesto
}
}

Figura 1.33: Resultado ejecución YearUtilitiesTest

Nota: Elaboración propia

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.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 43

Adicionalmente por restricción de nuestra aplicación debemos considerar que la contraseña


debe ser de máximo diez caracteres y para darle mayor consistencia debe contener al menos
una letra mayúscula. Para ello utilizamos el mismo proyecto “Tema2Sesion1b”.

Paso 1: Escribimos un test


package pe.edu.cibertec;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class TestPasswordValidator {


@Test
public void testValidLength() {
PasswordValidator pv = new PasswordValidator();
assertEquals(true, pv.isValid("Abcd123"));
}
}

Paso 2: Correr todos los tests


Nos da error pues la clase PasswordValidator no existe. Implementamos lo “mínimo necesario”:
package pe.edu.cibertec;

public class PasswordValidator {


public boolean isValid(String password) {
if (password.length() >= 7 && password.length() <= 10) {
return true;
}
else {
return false;
}
}
}

Paso 3: ¿Se puede mejorar el código?


Si.
¿Realmente necesitamos una instancia de PasswordValidator?
package pe.edu.cibertec;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class TestPasswordValidator {


@Test
public void testValidLength() {
assertEquals(true, PasswordValidator.isValid("Abcd123"));
}
}

Decisión de diseño: Método estático


package pe.edu.cibertec;

public class PasswordValidator {


public static boolean isValid(String password) {
if (password.length() >= 7 && password.length() <= 10) {
return true;
}
else {
return false;
}

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 44

}
}

¿Puede ser aún más simple?


package pe.edu.cibertec;

public class PasswordValidator {


public static boolean isValid(String password) {
return (password.length() >= 7 && password.length() <=
10);
}
}

Mantener constantes como el 7 y el 10 dentro del código puede ser peligroso


package pe.edu.cibertec;

public class PasswordValidator {


private final static int MIN_PW_LENGTH = 7;
private final static int MAX_PW_LENGTH = 10;
public static boolean isValid(String password) {
return (password.length() >= MIN_PW_LENGTH &&
password.length() <= MAX_PW_LENGTH);
}
}

Paso 1: Escribimos un test


@Test
public void testNotDigit() {
assertEquals(false, PasswordValidator.isValid("Abcdefg"));
}

Paso 2: Correr todos los tests


Nos da error pues el método isValid devuelve true cuando debería ser false. Implementamos lo
mínimo necesario:
package pe.edu.cibertec;

import java.util.regex.Pattern;

public class PasswordValidator {


private final static int MIN_PW_LENGTH = 7;
private final static int MAX_PW_LENGTH = 10;
public static boolean isValid(String password) {
return (password.length() >= MIN_PW_LENGTH &&
password.length() <= MAX_PW_LENGTH)&&
Pattern.matches(".*\\p{Digit}.*", password);
}
}

Paso 3: ¿Se puede mejorar el código?


No. Regresamos al Paso 1

Paso 1: Escribimos un test


@Test
public void testNotUpperCase() {
assertEquals(false, PasswordValidator.isValid("abcdef1"));
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 45

Paso 2: Correr todos los tests


Nos da error pues el método isValid devuelve true cuando debería ser false. Implementamos lo
mínimo necesario:
package pe.edu.cibertec;

import java.util.regex.Pattern;

public class PasswordValidator {


private final static int MIN_PW_LENGTH = 7;
private final static int MAX_PW_LENGTH = 10;
public static boolean isValid(String password) {
return (password.length() >= MIN_PW_LENGTH &&
password.length() <= MAX_PW_LENGTH)&&
Pattern.matches(".*\\p{Digit}.*", password)&&
password.toLowerCase()!=password;
}
}

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.*;

public class PasswordValidatorTest {


@Test
public void testValidLength() {
assertEquals(true, PasswordValidator.isValid("Abcd123"));
}
@Test
public void testNotDigit() {
assertEquals(false,
PasswordValidator.isValid("Abcdefg"));
}
@Test
public void testNotUpperCase() {
assertEquals(false,
PasswordValidator.isValid("abcdef1"));
}
@Test
public void testUpperCaseDigit() {
assertEquals(true,
PasswordValidator.isValid("AAbcdefee4"));
}
@Test
public void testUpperCaseNotDigit() {
assertEquals(false,
PasswordValidator.isValid("AAbcdefes"));
}
}

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 46

Figura 1.34: Resultado ejecución TestPasswordValidator

Nota: Elaboración propia

Ejercicio Propuesto: "Tipos de Triángulo"

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:

 Un triángulo es de tipo equilátero si sus tres lados son iguales.


 Un triángulo es de tipo isósceles si dos de sus lados son iguales.
 Un triángulo es de tipo escaleno si sus tres lados son diferentes.
 Un triángulo debe cumplir que un lado debe ser menor que la suma de los otros dos,
pero mayor que su diferencia. Si no cumple esta condición se dice que el triángulo no
existe.

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.

Código de la clase “Triángulo”:


package pe.edu.cibertec;

public class Triangulo {


private int lado1;
private int lado2;
private int lado3;

public Triangulo(int lado1, int lado2, int lado3) {


this.lado1 = lado1;
this.lado2 = lado2;
this.lado3 = lado3;
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 47

public int validarTipoTriangulo() {


if (!validarTriangulo()) {
return 0; // Triángulo no existe
} else if (lado1 == lado2 && lado2 == lado3) {
return 1; // Triángulo Equilatero
} else if (lado1 == lado2 || lado1 == lado3 || lado2 ==
lado3) {
return 2; // Triángulo Isosceles
} else if (lado1 != lado2 && lado1 != lado3 && lado3 !=
lado2) {
return 3; // Triángulo Escaleno
} else
return 0; // Triángulo no existe
}

public boolean validarTriangulo(){


if ( (lado1<(lado2+lado3)) && (lado1>Math.abs(lado2-
lado3)) ) {
if ( (lado2<(lado1+lado3)) && (lado2>Math.abs(lado1-
lado3)) ) {
if ( (lado3<(lado1+lado2)) &&
(lado3>Math.abs(lado1-lado2)) ) {
return true;
}
}
}
return false;
}
}

Código de la clase “TrianguloTest”:


package pe.edu.cibertec;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class TrianguloTest {


@Test
public void trianguloNoExisteTrue() {
System.out.println("Triangulo No Existe: 0, 20, 20");
Triangulo c = new Triangulo(0, 20, 20);
assertTrue(c.validarTipoTriangulo() == 0);
}

@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

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 48

public void trianguloEquilateroTrue() {


System.out.println("Triangulo Equilatero: 20, 20, 20");
Triangulo c = new Triangulo(20, 20, 20);
assertTrue(c.validarTipoTriangulo() == 1);
}

@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);

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 49

}
}
Figura 1.35: Resultado ejecución TrianguloTest

Nota: Elaboración propia

Ejercicio Propuesto: "Cálculo del Impuesto a la Renta"

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):

Escala UIT Tasas


Menos de 7 UIT 0%
Exceso 7 y hasta 12 UIT 8%
Exceso 12 y hasta 27 UIT 14%
Exceso 27 y hasta 42 UIT 17%
Exceso 42 y hasta 52 UIT 20%
Exceso de 52 UIT 30%

Considerar, que la UIT es configurable (S/. 4,950)


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.

Utilizamos el mismo proyecto “Tema2Sesion1b”. Código de la clase “ImpuestoRenta”:


package pe.edu.cibertec;

public class ImpuestoRenta {


private double uit = 3950;
private double sueldo;
private double excedente;

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 50

public ImpuestoRenta(double sueldo) {


this.sueldo = sueldo;
}

public double calcularImpuesto() {


double iR;
if (sueldo < (uit * 7)) {
return iR = 0;
} else if (sueldo >= (uit * 7) && sueldo <= (uit * 12)) {
excedente = sueldo - (uit * 7);
iR = excedente * 0.08;
return iR;
} else if (sueldo > (uit * 12) && sueldo <= (uit * 27)) {
excedente = sueldo - (uit * 12);
iR = excedente * 0.14 + uit * 5 * 0.08;
return iR;
} else if (sueldo > (uit * 27) && sueldo <= (uit * 42)) {
excedente = sueldo - (uit * 27);
iR = excedente * 0.17 + uit * 15 * 0.14 + uit * 5 *
0.08;
return iR;
} else if (sueldo > (uit * 42) && sueldo <= (uit * 52)) {
excedente = sueldo - (uit * 42);
iR = excedente * 0.20 + uit * 15 * 0.17 + uit * 15 *
0.14 + uit * 5 * 0.08;
return iR;
} else {
excedente = sueldo - (uit * 52);
iR = excedente * 0.30 + uit * 10 * 0.20 + uit * 15 *
0.17 + uit * 15 * 0.14 + uit * 5 * 0.08;
return iR;
}
}
}

Código de la clase “ImpuestoRentaTest”:


package pe.edu.cibertec;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class ImpuestoRentaTest {


@Test
public void tasa0True() {
ImpuestoRenta c = new ImpuestoRenta(4000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 0%: " + iR);
assertTrue(c.calcularImpuesto() == 0.00);
}

@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

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 51

public void tasa0Equals() {


ImpuestoRenta c = new ImpuestoRenta(4000);
double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 0%: " + iR);
assertEquals(c.calcularImpuesto() , 0.00);
}

@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);

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 52

@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);

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 53

double iR = Math.rint(c.calcularImpuesto()*100)/100;
System.out.println("Tasa 30%: " + iR);
assertEquals(c.calcularImpuesto() , 56227.50);
}
}

Figura 1.36: Resultado ejecución ImpuestoRentaTest

Nota: Elaboración propia

1.2.2. Creación y Ejecución de una prueba unitaria de regular complejidad con JUnit
en IntelliJ

Utilizando la técnica Test Driven Development desarrolle un programa que implemente un


carrito de compras.
Para ello creamos un proyecto llamado “Tema2Sesion1c” y creamos la clase “Product”:
package pe.edu.cibertec;

public class Product {


private String code;
private String title;
private String description;
private double price;
public Product(String code, String title, String description,
double price) {
super();
this.code = code;
this.title = title;
this.description = description;
this.price = price;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getTitle() {
return title;

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 54

}
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;
}
}

Figura 1.37: Clase Product

Nota: Elaboración propia

Creamos la clase “ShoppingCart”:


package pe.edu.cibertec;

import java.util.ArrayList;
import java.util.Iterator;

public class ShoppingCart {


private ArrayList items;

public ShoppingCart() {
items = new ArrayList();
}

public double getBalance() {


double balance = 0.00;
for (Iterator i = items.iterator(); i.hasNext();) {
Product item = (Product) i.next();
balance += item.getPrice();

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 55

}
return balance;
}

public void addItem(Product item) {


items.add(item);
}

public void removeItem(Product item) {


items.remove(item);
}

public int getItemCount() {


return items.size();
}

public void empty() {


items.clear();
}
}

Figura 1.38: Clase ShoppingCart

Nota: Elaboración propia

Generamos la clase de Test “ProductTest” con Junit.


package pe.edu.cibertec;

import org.junit.jupiter.api.*;

import static org.junit.jupiter.api.Assertions.*;

class ProductTest {
private Product prod1;

@BeforeAll
static void setUpBeforeClass() throws Exception {
System.out.println("Ejecutando setUpBeforeClass");
}

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 56

@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);
}
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 57

Figura 1.39: Ejecución de la clase ProductTest

Nota: Elaboración propia

Generamos la clase de Test “ShoppingCartTest” con JUnit.


package pe.edu.cibertec;

import org.junit.jupiter.api.*;

import static org.junit.jupiter.api.Assertions.*;

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;
}

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 58

@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());
}
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 59

Figura 1.40: Ejecución de la clase ShoppingCartTest

Nota: Elaboración propia

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:

- Spring Data JPA


- Lombok
- MS SQL Server Driver
- Spring Web

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 60

Figura 1.41: Generando proyecto Tema2Sesion2

Nota: Elaboración propia

Levante el proyecto en IntelliJ y genere los siguientes paquetes:

- controller
- model
- repo
- service
- service.impl

Figura 1.42: Estructura del proyecto Tema2Sesion2

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 61

Genere la clase “Persona” en el paquete model:

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;
}

Genere la clase “Distrito” en el paquete model:


package pe.edu.cibertec.Tema2Sesion2.model;

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;
}

Genere la interface “IPersonaRepo” en el paquete repo:


package pe.edu.cibertec.Tema2Sesion2.repo;

import org.springframework.data.jpa.repository.JpaRepository;

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 62

import pe.edu.cibertec.Tema2Sesion2.model.Persona;

public interface IPersonaRepo extends JpaRepository<Persona,


Integer> {
}
Genere la interface “IPersonaService” en el paquete service:
package pe.edu.cibertec.Tema2Sesion2.service;

import pe.edu.cibertec.Tema2Sesion2.model.Persona;

import java.util.List;

public interface IPersonaService {


public List<Persona> listarPersonas();
public Persona obtenerPersona(Integer id);
public Persona registrarPersona(Persona persona);
public Persona modificarPersona(Persona persona);
public String eliminarPersona(Integer id);
}

Genere la clase “PersonaServiceImpl” en el paquete service.impl:


package pe.edu.cibertec.Tema2Sesion2.service.impl;

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;

public List<Persona> listarPersonas() {


return personaRepo.findAll();
}

public Persona obtenerPersona(Integer id) {


return personaRepo.findById(id).get();
}

public Persona registrarPersona(Persona persona) {


return personaRepo.save(persona);
}

public Persona modificarPersona(Persona persona) {


return personaRepo.save(persona);
}

public String eliminarPersona(Integer id) {


personaRepo.deleteById(id);
return "Persona eliminada";
}

}
Genere la clase “PersonaController” en el paquete controller:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 63

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);
}
}

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 64

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 * from sysobjects where name='distrito' and


xtype='U')
create table distrito (distrito_id int primary key,
distrito_nombre varchar(255));

Dentro de la carpeta bd crear el archivo “data.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;

En el archivo “application.properties” agregue el siguiente código (modifique el password de la


Base de Datos):

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-

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 65

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

En SQL Server cree la BD “Tema2Sesion2”, asimismo asegúrese que en las propiedades de


seguridad del servidor esté habilitado la opción “SQL Server and Windows Authentication mode”
en la opción “Server authentication”

Figura 1.43: Estructura del proyecto Tema2Sesion2

Nota: Elaboración propia

Habilite el usuario sa y cámbiele el password:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 66

Figura 1.44: Estructura del proyecto Tema2Sesion2

Nota: Elaboración propia

Figura 1.45: Estructura del proyecto Tema2Sesion2

Nota: Elaboración propia

También en “SQL Server Configuration Manager”, en la opción “Protocols for MSSQLSERVER”


habilite el protocolo “TCP/IP” y el puerto 1433:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 67

Figura 1.46: SQL Server Configuration Manager

Nota: Elaboración propia

Figura 1.47: Sql Server Configuration Manager

Nota: Elaboración propia

Inicie el proyecto y revise que el proyecto levante sin errores:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 68

Figura 1.48: Iniciando el proyecto

Nota: Elaboración propia

Revisar en la BD que se haya generado las tablas distrito y persona, y dentro de la tabla distrito
visualizar que exista data:

Figura 1.49: Tablas distrito y persona

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 69

Figura 1.50: Data generada en tabla distrito

Nota: Elaboración propia

Utilizando el software Postman genera los siguientes request, se adjunta el CURL para su
generación:

- Curl para el request “listarPersonas”:


curl --location 'http://localhost:8080/api/v1/persona'
- Curl para el request “obtenerPersona”:
curl --location 'http://localhost:8080/api/v1/persona/1'
- Curl para el request “registrarPersona”:
curl --location 'http://localhost:8080/api/v1/persona' \
--header 'Content-Type: application/json' \
--data '{
"nombre": "julio",
"apellido": "casas",
"direccion": "mi direccion",
"telefono": "987654321",
"distrito":{
"id": 1,
"nombre": "Lima"
}
}'
- Curl para el request “modificarPersona”:
curl --location --request PUT 'http://localhost:8080/api/v1/persona' \
--header 'Content-Type: application/json' \
--data '{
"id": 1,
"nombre": "pedro",
"apellido": "casillas",
"direccion": "mi direccion",
"telefono": "987654321",
"distrito": {
"id": 1,

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 70

"nombre": "Lima"
}
}'
- Curl para el request “eliminarPersona”:
curl --location --
request DELETE 'http://localhost:8080/api/v1/persona/1'

Validar los requests generados:


Figura 1.51: Ejecución de request registrarPersona

Nota: Elaboración propia

Figura 1.52: Ejecución de request modificarPersona

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 71

Figura 1.53: Ejecución de request obtenerPersona

Nota: Elaboración propia

Figura 1.54: Ejecución de request listarPersonas

Nota: Elaboración propia

Figura 1.55: Ejecución de request eliminarPersona

Nota: Elaboración propia

Ahora implementaremos las pruebas unitarias respectivas con Mockito, para ello generamos la
clase PersonaServiceImplTest:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 72

Figura 1.56: Clase PersonaServiceImplTest

Nota: Elaboración propia

Código de 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;

import static org.junit.jupiter.api.Assertions.*;


import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;

@WebMvcTest(PersonaServiceImplTest.class)
class PersonaServiceImplTest {

@InjectMocks
PersonaServiceImpl personaServiceImpl;

@Mock
IPersonaRepo personaRepo;

@BeforeEach
void setUp() {
personaServiceImpl = new PersonaServiceImpl(personaRepo);
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 73

@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();

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 74

distrito.setId(1);
distrito.setNombre("Lima");

Persona persona1 = new Persona();


persona1.setId(1);
persona1.setNombre("mynombre1");
persona1.setApellido("myApellido1");
persona1.setDireccion("myDireccion1");
persona1.setTelefono("987654321");
persona1.setDistrito(distrito);

Persona persona2 = new Persona();


persona2.setId(1);
persona2.setNombre("mynombre2");
persona2.setApellido("myApellido2");
persona2.setDireccion("myDireccion2");
persona2.setTelefono("963852741");
persona2.setDistrito(distrito);

List<Persona> personas = new ArrayList<>();


personas.add(persona1);
personas.add(persona2);

return personas;
}

Persona generarPersona(){
Distrito distrito = new Distrito();
distrito.setId(1);
distrito.setNombre("Lima");

Persona persona = new Persona();


persona.setId(1);
persona.setNombre("mynombre1");
persona.setApellido("myApellido1");
persona.setDireccion("myDireccion1");
persona.setTelefono("987654321");
persona.setDistrito(distrito);

return persona;
}
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 75

Figura 1.57: Ejecución de la clase PersonaServiceImplTest

Nota: Elaboración propia

Generamos la clase PersonaControllerTest:


Figura 1.58: Clase PersonaServiceImplTest

Nota: Elaboración propia

Código de la clase PersonaControllerTest:

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

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 76

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;

import static org.junit.jupiter.api.Assertions.*;


import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.*;

@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());

List<Persona> personasResponse = (List<Persona>)


responseEntity.getBody();
assertEquals(2, personasResponse.size());
}

@Test
void obtenerPersona() {
Persona persona = generarPersona();

when(personaServiceImpl.obtenerPersona(anyInt())).thenReturn(pers
ona);
ResponseEntity<?> responseEntity =
personaController.obtenerPersona(1);

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 77

assertEquals(HttpStatus.OK,
responseEntity.getStatusCode());

Persona personaResponse = (Persona)


responseEntity.getBody();
assertEquals(1, personaResponse.getId());
}

@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());

Persona personaResponse = (Persona)


responseEntity.getBody();
assertEquals(1, personaResponse.getId());
}

@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());

Persona personaResponse = (Persona)


responseEntity.getBody();
assertEquals(1, personaResponse.getId());
}

@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");

Persona persona1 = new Persona();


persona1.setId(1);
persona1.setNombre("mynombre1");

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 78

persona1.setApellido("myApellido1");
persona1.setDireccion("myDireccion1");
persona1.setTelefono("987654321");
persona1.setDistrito(distrito);

Persona persona2 = new Persona();


persona2.setId(2);
persona2.setNombre("mynombre2");
persona2.setApellido("myApellido2");
persona2.setDireccion("myDireccion2");
persona2.setTelefono("963852741");
persona2.setDistrito(distrito);

List<Persona> personas = new ArrayList<>();


personas.add(persona1);
personas.add(persona2);

return personas;
}

Persona generarPersona(){
Distrito distrito = new Distrito();
distrito.setId(1);
distrito.setNombre("Lima");

Persona persona = new Persona();


persona.setId(1);
persona.setNombre("mynombre1");
persona.setApellido("myApellido1");
persona.setDireccion("myDireccion1");
persona.setTelefono("987654321");
persona.setDistrito(distrito);

return persona;
}
}

Figura 1.50: Ejecución de la clase PersonaControllerTest

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 79

Resumen
1. TDD es una Técnica de Desarrollo basada en “testear” primero:

 Es más que una técnica de testing


 Es más que una técnica de programación
 Es más que una técnica de diseño
 Es todo en junto

2. TDD surge como una de las técnicas de eXtreme Programming.

3. Todo test unitario debe ser: Atómico, Independiente, Inocuo y Rápido.

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.

7. Un mock valida comportamiento en la colaboración, mientras que el stub simplemente


simula respuestas a consultas.

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

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 80

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

3.2 Tema 6 : Tema 6: Pruebas Funcionales web utilizando Selenium y lenguaje


Gherkin integrado con Cucumber y Serenity
3.2.1 : Creación y Ejecución de una prueba funcional web simple utilizando
Selenium y lenguaje Gherkin integrado con Cucumber y Serenity
3.2.2 : Creación y Ejecución de una prueba funcional web de regular complejidad
utilizando Selenium y lenguaje Gherkin integrado con Cucumber y
Serenity
3.2.3 : Creación y Ejecución de una prueba funcional web avanzada utilizando
Selenium y lenguaje Gherkin integrado con Cucumber y Serenity

ACTIVIDADES PROPUESTAS

• Los alumnos interiorizan cómo identificar pruebas integrales en una aplicación.


• Los alumnos elaboran pruebas integrales con lenguaje Gherkin, Cucumber,
Serenity y Rest Assured.
• Los alumnos aplican BDD en la elaboración de pruebas integrales.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 81

2.1. INTRODUCCIÓN A BDD


2.1.1. BDD

BDD (Behavior Driven Development) conocido como "desarrollo guiado por


comportamiento", es un proceso de software ágil que busca la colaboración y
entendimiento entre desarrolladores, gestores de proyecto y equipo de negocio.

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)

¿Cuándo usar el desarrollo guiado por comportamiento?

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.

El BDD permite a todos los implicados entender el proceso desarrollado y el contenido


del código fuente. De hecho, este desarrollo le indica al código qué tiene que hacer y lo
hace en un lenguaje natural entendido por todo el mundo.

Beneficios del BDD

 Aumenta la colaboración entre las partes involucradas. Al tener todos un mismo


lenguaje de entendimiento, todas las partes sienten que forman un único equipo
y mejora la colaboración entre ellas.
 Se prioriza el valor de negocio. Se establecen prioridades con los clientes y el
lenguaje BDD permite a los desarrolladores entender e involucrarse más con los
objetivos comerciales, logrando un resultado óptimo a las necesidades del
cliente.
 Reducción de riesgo en el proyecto. Como todas las partes implicadas saben de
qué se está hablando en todo momento y cuentan con una visión global del
proyecto, se corren menos riesgos en el desarrollo e implementación del
proyecto.
 Creación de metodologías ágiles de trabajo. Al explicarse todos los
requerimientos y escenarios posibles, el desarrollo posterior es mucho más
sencillo, ya que se intenta comprender el comportamiento del sistema en todas
sus vertientes.

¿Qué tener en cuenta para aplicarlo?

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)

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 82

2.1.2. Lenguaje Gherkin

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:

 Feature. Describe la funcionalidad que hay que desarrollar.


 Scenario. Son las características que se dan para lograr la funcionalidad.
 Given. Hace referencia a las predicciones para que se puedan ejecutar las
distintas acciones.
 When. Son las condiciones de las acciones a ejecutar.
 Then. Es el resultado de las acciones ejecutadas.

Además de estos cinco elementos Gherkin, existe el patrón Role-Feaure-Reason. Esto es


muy sencillo si jugamos con la estructura “Como [x], quiero [y] para que [z]” y nos
ayudará a elaborar la funcionalidad o Feature dentro del lenguaje Gherkin:

 Cómo. Especifica el tipo de usuario de la acción.


 Quiero. Hace referencia a las necesidades de ese usuario.
 Para qué. Es el objetivo final que desea cumplir.

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.

En este caso, el lenguaje Gherkin quedaría especificado de la siguiente manera en BDD:

 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

Es un framework que da soporte para aplicar la metodología de desarrollo BDD (Behavior


Driven Development) que nos permite elaborar pruebas unitarias a partir de criterios de
aceptación, fácilmente entendibles por todos los intervinientes del proceso. A través de
Cucumber, el analista podrá definir un conjunto de casos de uso que permitan validar el
desarrollo realizado. Estos casos de uso tendrán su correlación con escenarios de
Cucumber, los cuales estarán implementados posteriormente en un lenguaje de
programación dominado por el ingeniero de calidad o testing.

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.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 83

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:

 Las pruebas que hemos realizado.


 Visualización de todos los features, escenarios y steps asociados a las pruebas.
 Conocer información relativa a la ejecución de estas: cuál ha sido el resultado de
su ejecución, el tiempo necesario para su ejecución, etc.
 Visualización de resultados mediante gráficos (ISO 9001:2015, 2023)

2.1.4. Rest Assured

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:

 El código de respuesta HTTP (response code)


 Las cabeceras HTTP de la respuesta
 El contenido de la respuesta en JSON o XML (payload)

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:

 Escribir test flexibles y fáciles de mantener.


 Generar informes ilustrativos sobre las pruebas.
 Ajustar las pruebas automatizadas a las necesidades del proyecto.
 Ver cuánto de la aplicación se está probando.

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 84

¿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.

¿Qué es Screen Play?

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.

¿Por qué Screen Play y no Page Object?

El patrón Page Object no cumple con algunos de los principios SOLID recomendados para
realizar código flexible y mantenible.

Figura 2.1: Arquitectura de Screen Play

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.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 85

5) Interactions: Se encargan de las interacciones con la interfaz de usuario. Muchas


interacciones ya fueron desarrolladas y se encuentran disponibles gracias a las
librerías de screenplay y de serenity BDD.
6) Questions: Esta capa gestiona los Asserts o verificaciones de las pruebas las cuales
son el fin último de las mismas, ya que con estas capturamos la información de la
interfaz que puede ser usada.
7) User interface: Almacena las clases en las cuales se mapean los elementos de la
interfaz de usuario o inputs o botones.

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).

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 86

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

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 87

2.2. PRUEBAS INTEGRALES CON LENGUAJE GHERKIN INTEGRADO CON


CUCUMBER, SERENITY Y REST ASSURED
2.2.1. Creación y Ejecución de una prueba integral simple 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 el api de códigos postales para países (api.zippopotam.us).
Para ello creamos un proyecto llamado “Tema4Ejercicio1” sin agregar dependencias:

Figura 2.2: Generando proyecto Tema4Ejercicio1

Nota: Elaboración propia

Levante el proyecto en IntelliJ y genere los siguientes paquetes en el paquete src.test:


- java.cucumber.codigopostal
- resources.codigopostal

Elimine de los paquetes src.main.java y src.main.resources todas las clases y recursos generados.

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 88

Figura 2.3: Estructura del proyecto Tema4Ejercicio1

Nota: Elaboración propia

En el archivo pom.xml coloque el siguiente código y recargue Maven

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pe.edu.cibertec</groupId>
<artifactId>Tema4Ejercicio1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Tema4Ejercicio1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>20</java.version>
<logback.version>1.4.7</logback.version>
<serenity.version>2.6.0</serenity.version>

<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>

<!-- 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>

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 89

<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>

<!-- librerias usadas directamente por serenity-core-->


<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>${rest.assured.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-path</artifactId>
<version>${rest.assured.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>xml-path</artifactId>
<version>${rest.assured.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy</artifactId>
</exclusion>

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 90

<exclusion>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-xml</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- Librerias generales de testing -->


<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<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>

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 91

Figura 2.4: Recargando Maven

Nota: Elaboración propia

Genere la clase “CucumberTestSuite” en el paquete src.test.java.cucumber con el siguiente


código:

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 {}

Genere la clase “CodigoPostalAPI” en el paquete src.test.java.cucumber.codigopostal con el


siguiente código:

package cucumber.codigopostal;

import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Step;

public class CodigoPostalAPI {

private static String UBICACION_POR_CODIGO_POSTAL_Y_PAIS =


"http://api.zippopotam.us/{pais}/{codigopostal}";

@Step("Obtener ubicacion por codigo postal {0} en pais {1}")


public void buscarUbicacionPorCodigoPostalYPais(String
codigopostal, String pais) {
SerenityRest.given()
.pathParam("codigopostal", codigopostal)
.pathParam("pais", pais)
.get(UBICACION_POR_CODIGO_POSTAL_Y_PAIS);

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 92

}
}
Genere la clase “UbicacionResponse” en el paquete src.test.java.cucumber.codigopostal con el
siguiente código:

package cucumber.codigopostal;

public class UbicacionResponse {


public static final String PAIS = "'country'";
public static final String UBICACION = "'places'[0].'place
name'";
}

La clase “UbicacionResponse” tiene la estructura del response del api: api.zippopotam.us

Figura 2.5: Response api.zippopotam.us

Nota: Elaboración propia

Genere la clase “CodigoPostalStepDefinitions” en el paquete


src.test.java.cucumber.codigopostal con el siguiente código:

package cucumber.codigopostal;

import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import net.thucydides.core.annotations.Steps;

import static net.serenitybdd.rest.SerenityRest.restAssuredThat;


import static org.hamcrest.Matchers.equalTo;

public class CodigoPostalStepDefinitions {

@Steps
CodigoPostalAPI codigoPostalAPI;

@When("Busco un código postal {word} para el codigo de pais


{word}")

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 93

public void buscarCodigoPostal(String postCode, String


country) {

codigoPostalAPI.buscarUbicacionPorCodigoPostalYPais(postCode,
country);
}

@Then("la ubicacion deberia ser {} en {}")


public void ubicacionDeberiaSer(String ubicacion, String
pais) {
restAssuredThat(response -> response.statusCode(200));
restAssuredThat(response ->
response.body(UbicacionResponse.PAIS, equalTo(pais)));
restAssuredThat(response ->
response.body(UbicacionResponse.UBICACION, equalTo(ubicacion)));
}
}

Genere el archivo “logback-test.xml” en el paquete src.test.resources con el siguiente código:

<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>

<logger name="root" level="DEBUG"/>


<logger name="net.serenitybdd" level="WARN"/>
<logger name="net.thucydides" level="WARN"/>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>

</configuration>

Genere el archivo “buscar_codigo_postal.feature” en el paquete


src.test.resources.features.codigopostal con el siguiente código:

Feature: Buscando codigos postales

Scenario Outline: Buscar ubicaciones en EE. UU. por codigo


postal
When Busco un código postal <Codigo Postal> para el codigo de
pais <Codigo Pais>
Then la ubicacion deberia ser <Ubicacion> en <Pais>
Examples:
| Codigo Postal | Codigo Pais | Pais | Ubicacion
|
| 10000 | US | United States | New York
City |
| 90210 | US | United States | Beverly
Hills |

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 94

| 13001 | FR | France | Marseille


01 |
Figura 2.6: Archivo buscar_codigo_postal.feature

Nota: Elaboración propia

Ejecutar el programa CucumberTestSuite:

Figura 2.7: Ejecutar programa CucumberTestSuite

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 95

Resultado de la ejecución del programa CucumberTestSuite:

Figura 2.8: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

Revisar en la carpeta target/site/serenity la creación de un archivo .html con el resultado de la


ejecución:

Figura 2.9: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 96

2.2.2. Creación y Ejecución de una prueba integral de regular complejidad 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 consultar usuarios con GO REST (https://gorest.co.in/).

Para ello creamos un proyecto llamado “Tema4Ejercicio2 sin agregar dependencias:

Figura 2.10: Generando proyecto Tema4Ejercicio2

Nota: Elaboración propia

Levante el proyecto en IntelliJ y genere los siguientes paquetes en el paquete src.test:

- java.cucumber.usuario.stepDef
- java.cucumber.utils
- resources.features.usuario
- resources.JSON.Schema

Elimine de los paquetes src.main todos las clases y recursos generados.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 97

Figura 2.11: Estructura del proyecto Tema4Ejercicio2

Nota: Elaboración propia

En el archivo pom.xml coloque el siguiente código y recargue Maven

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<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>

<!-- Dependencias de Serenity -->


<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-core</artifactId>
<version>${serenity.version}</version>
<scope>test</scope>
</dependency>

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 98

<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>

<!-- librerias usadas directamente por serenity-core-->


<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>${rest.assured.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-path</artifactId>
<version>${rest.assured.version}</version>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>xml-path</artifactId>
<version>${rest.assured.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy</artifactId>

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 99

</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>

<!-- Librerias generales de testing -->


<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>java-hamcrest</artifactId>
<version>2.0.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<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>

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 100

Genere la clase “CucumberTestSuite” en el paquete src.test.java.cucumber con el siguiente


código:
package cucumber;

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 {}

Genere la clase “UsuarioAPI” en el paquete src.test.java.cucumber con el siguiente código:

package cucumber;

import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Step;
import cucumber.utils.Constantes;

public class UsuarioAPI {


public static String GET_USER = Constantes.BASE_URL +
"/public/v2/users";

@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);

}
}

Genere la clase “Constantes” en el paquete src.test.java.cucumber.utils con el siguiente código:

package cucumber.utils;

public class Constantes {


public static final String BASE_URL = "https://gorest.co.in";
public static final String DIR =
System.getProperty("user.dir"); // get location project
public static String JSON_SCHEMA = DIR +
"/src/test/resources/JSON/Schema"; // path json request
}

Genere la clase “GeneralStepDef” en el paquete src.test.java.cucumber.usuario.stepDef con el


siguiente código:

package cucumber.usuario.stepDef;

import io.cucumber.java.en.Then;

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 101

import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Steps;
import cucumber.UsuarioAPI;

public class GeneralStepDef {


@Steps
UsuarioAPI usuarioAPI;
@Then("debería devolver el codigo {int}")
public void deberiaRetornarCodigo(int statusCode) {
SerenityRest.then().assertThat().statusCode(statusCode);
}

Genere la clase “ObtenerUsuariorStepDef” en el paquete


src.test.java.cucumber.usuario.stepDef con el siguiente código:

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;

import static org.hamcrest.Matchers.*;

public class ObtenerUsuariorStepDef {


@Steps
UsuarioAPI usuarioAPI;

@Given("el usuario sin parametros")


public void usuarioSinParametros() {
SerenityRest.given();
}

@When("se envia solicitud de usuario")


public void enviarSolicitudUsuario() {
SerenityRest.when().get(UsuarioAPI.GET_USER);
}

@And("validar el usuario de la lista de esquemas json")


public void validarUsuarioJsonEsquema() {
File jsonSchema = new File(Constantes.JSON_SCHEMA +
"/ResponseUserSchemaValid.json");
SerenityRest.then()
.assertThat()

.body(JsonSchemaValidator.matchesJsonSchema(jsonSchema));
}

@Given("el usuario con genero {string}")


public void usuarioConParametro(String genero) {
usuarioAPI.obtenerUSuarios("", "", genero, "");
}

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 102

@And("validar el genero del usuario cual debe ser {string}")


public void validarGeneroUsuario(String parameter) {
SerenityRest.then()
.assertThat()
.body("gender",
everyItem(hasToString(parameter)));
}

@Given("el usuario con nombre {string}")


public void usuarioConNombre(String nombre) {
usuarioAPI.obtenerUSuarios(nombre, "", "", "");
}

@And("validar el nombre de usuario debe contener {string}")


public void validarNombreUsuarioDebeContener(String name) {
SerenityRest.then()
.assertThat()
.body("name",
everyItem(containsStringIgnoringCase(name)));
}

@Given("el usuario con estado {string}")


public void usuarioConEstado(String estado) {
usuarioAPI.obtenerUSuarios("", "", "", estado);
}

@And("validar el estado del usuario debe ser {string}")


public void validarEstadoUsuario(String status) {
SerenityRest.then()
.assertThat()
.body("status", everyItem(hasToString(status)));
}

@Given("el usuario con email {string}")


public void usuarioConEmail(String email) {
usuarioAPI.obtenerUSuarios("", email, "", "");
}

@And("validar el email del usuario debe contener {string}")


public void validarEmailUsuariDebeContener(String email) {
SerenityRest.then()
.assertThat()
.body("email",
everyItem(containsStringIgnoringCase(email)));
}

Genere el archivo “logback-test.xml” en el paquete src.test.resources con el siguiente código:

<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>

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 103

<logger name="root" level="DEBUG"/>


<logger name="net.serenitybdd" level="WARN"/>
<logger name="net.thucydides" level="WARN"/>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>

</configuration>

Genere el archivo “obtener_usuario.feature” en el paquete src.test.resources.features.usuario


con el siguiente código:

@Test
Feature: Obtener usuarios

Scenario: Obtener usuario sin parametro


Given el usuario sin parametros
When se envia solicitud de usuario
Then debería devolver el codigo 200
And validar el usuario de la lista de esquemas json

Scenario Outline: Obtener usuario con parametro de genero


Given el usuario con genero "<genero>"
When se envia solicitud de usuario
Then debería devolver el codigo 200
And validar el usuario de la lista de esquemas json
And validar el genero del usuario cual debe ser "<genero>"
Examples:
| genero |
| male |

Scenario Outline: Obtener usuario con parametro de nombre


Given el usuario con nombre "<nombre>"
When se envia solicitud de usuario
Then debería devolver el codigo 200
And validar el usuario de la lista de esquemas json
And validar el nombre de usuario debe contener "<nombre>"
Examples:
| nombre |
| oki |

Scenario Outline: Obtener usuario con parametro de estado


Given el usuario con estado "<estatus>"
When se envia solicitud de usuario
Then debería devolver el codigo 200
And validar el usuario de la lista de esquemas json
And validar el estado del usuario debe ser "<estatus>"
Examples:
| estatus |
| active |
| inactive |

Scenario Outline: Obtener usuario con parametro de email


Given el usuario con email "<email>"
When se envia solicitud de usuario
Then debería devolver el codigo 200
And validar el usuario de la lista de esquemas json
And validar el email del usuario debe contener "<email>"
Examples:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 104

| email |
| @johnston |

Genere el archivo “ResponseError.json” en el paquete src.test.resources.JSON.Schema con el


siguiente código:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"message": {
"type": "string"
}
},
"required": [
"message"
]
}

Genere el archivo “ResponseUserSchemaValid.json” en el paquete


src.test.resources.JSON.Schema con el siguiente código:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": [
{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
},
"gender": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"id",
"name",
"email",
"gender",
"status"
]
}
]
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 105

Ejecutar el programa CucumberTestSuite:

Figura 2.12: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

Revisar en la carpeta target/site/serenity la creación de 5 archivos .html con el resultado de la


ejecución:

Figura 2.13: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 106

Figura 2.14: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

Figura 2.15: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 107

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.

Levante el proyecto en IntelliJ y genere el siguiente paquete en el paquete src.test:


- resources.JSON.Schema

Actualice la clase “UsuarioAPI” en el paquete src.test.java.cucumber con el siguiente código:

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;

public class UsuarioAPI {


public static String GET_USER = Constantes.BASE_URL +
"/public/v2/users";
public static String POST_USER = Constantes.BASE_URL +
"/public/v2/users";
public static String PUT_USER = Constantes.BASE_URL +
"/public/v2/users/{user_id}";
public static String DELETE_USER = Constantes.BASE_URL +
"/public/v2/users/{user_id}";

@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("Actualizar usuari con string json")


public void actualizarUsuario(String json, String id) {
SerenityRest.given()
.header("Authorization", "Bearer " +
Constantes.TOKEN)
.pathParam("user_id", id)
.contentType(ContentType.JSON)
.body(json);

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 108

@Step("Actualizar usuario con file json")


public void actualizarUsuario(File json, String id) {
SerenityRest.given()
.header("Authorization", "Bearer " +
Constantes.TOKEN)
.pathParam("user_id", id)
.contentType(ContentType.JSON)
.body(json);
}

@Step("Actualizar usuario sin token")


public void actualizarUsuarioSinToken(String json, String id)
{
SerenityRest.given()
.pathParam("user_id", id)
.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);
}
}

Actualice la clase “Constantes” en el paquete src.test.java.cucumber.utils con el siguiente


código:

package cucumber.utils;

public class Constantes {


public static final String BASE_URL = "https://gorest.co.in";
public static final String DIR =
System.getProperty("user.dir"); // get location project
public static String JSON_SCHEMA = DIR +
"/src/test/resources/JSON/Schema"; // path json request
public static final String TOKEN =
"9fbf871c282cbedef5320ba6f2752f708048a14705713b91a4011e3f765f6196
";
public static String JSON_REQUEST = DIR +
"/src/test/resources/JSON/Request"; // path json request
}

Genere la clase “Payload” en el paquete src.test.java.cucumber.utils con el siguiente código:

package cucumber.utils;

public class Payload {


public String randomBodyRequestPostUser() {
String jsonString;
String characters = "abcdefghijklmnopqrstuvwxyz";
StringBuilder stringBuilder = new StringBuilder();
int nameLength = 7;
int counter = 0;
while(counter < nameLength){

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 109

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;
}

public String customBodyRequestPostUser(String name, String


gender, String email, String status) {
String jsonString;
jsonString = "{";
jsonString += "\"name\": \"" + name + "\",";
jsonString += "\"gender\": \"" + gender + "\",";
jsonString += "\"email\": \"" + email + "\",";
jsonString += "\"status\": \"" + status + "\"";
jsonString += "}";

return jsonString;
}
}

Actualice la clase “GeneralStepDef” en el paquete src.test.java.cucumber.usuario.stepDef con el


siguiente código:

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;

import static org.hamcrest.Matchers.equalTo;

public class GeneralStepDef {


@Steps
UsuarioAPI usuarioAPI;
@Then("debería devolver el codigo {int}")
public void deberiaRetornarCodigo(int statusCode) {
SerenityRest.then().assertThat().statusCode(statusCode);
}

@And("mensaje del response body message deberia ser


{string}")
public void respuestaResponseBodyDeberiaSer(String message) {
SerenityRest.then()
.body("message", equalTo(message));

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 110

}
@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));
}
}

Genere la clase “ActualizarUsuarioStepDef” en el paquete


src.test.java.cucumber.usuario.stepDef con el siguiente código:

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;

public class ActualizarUsuarioStepDef {


@Steps
UsuarioAPI usuarioAPI;

@Given("actualizar usuario con ID valido and con request body


valido")
public void updateUserWithValidIDAndRequestBody() {
Response response =
SerenityRest.given().get(usuarioAPI.GET_USER);
JsonPath jsonPathEvaluator = response.jsonPath();
Gson gson = new Gson();
String json =
gson.toJson(jsonPathEvaluator.getList("").get(0));
usuarioAPI.actualizarUsuario(json,
jsonPathEvaluator.get("[0].id").toString());
}

@When("enviar solicitud actualizar usuario")


public void enviarSolicituActualizarUsuario() {
SerenityRest.when().patch(usuarioAPI.PUT_USER);
}

@And("validar usuario json schema")


public void validarJsonSchemaActualizarUsuario() {
File jsonSchema = new File(Constantes.JSON_SCHEMA +
"/ResponseUpdateUserSchemaValid.json");
SerenityRest.then()
.assertThat()

.body(JsonSchemaValidator.matchesJsonSchema(jsonSchema));

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 111

@Given("actualizar usuario con {string} como ID y {string}


request body")
public void actualizarUsuarioConIDYRequestBody(String id,
String description) {
File json = new File(Constantes.JSON_REQUEST +
"/ValidRequestUpdateUser.json");
if(description.equals("invalid")){
json = new File(Constantes.JSON_REQUEST +
"/InvalidRequestUpdateUser.json");
}
usuarioAPI.actualizarUsuario(json, id);
}

@Given("actualizar usuario sin token")


public void actualizarUsuarioSinToken() {
Response response =
SerenityRest.given().get(usuarioAPI.GET_USER);
JsonPath jsonPathEvaluator = response.jsonPath();
Gson gson = new Gson();
String json =
gson.toJson(jsonPathEvaluator.getList("").get(0));
usuarioAPI.actualizarUsuarioSinToken(json,
jsonPathEvaluator.get("[0].id").toString());
}
}

Genere la clase “CrearUsuarioStepDef” en el paquete src.test.java.cucumber.usuario.stepDef


con el siguiente código:

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;

public class CrearUsuarioStepDef {


@Steps
UsuarioAPI usuarioAPI;

Payload payload = new Payload();


@Given("deseo crear un usuario con request body valido")
public void crearUsuarioConRequestBodyValido() {

usuarioAPI.crearUsuario(payload.randomBodyRequestPostUser());
}

@When("envio request para crear usuario")


public void enviarRequestCrearUsuario() {
SerenityRest.when().post(usuarioAPI.POST_USER);
}

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 112

@And("validar json schema crear usuario")


public void validarJsonSchemaCrearUsuario() {
File jsonSchema = new File(Constantes.JSON_SCHEMA +
"/ResponseCreateUserSchemaValid.json");
SerenityRest.then()
.assertThat()

.body(JsonSchemaValidator.matchesJsonSchema(jsonSchema));
}

@Given("deseo crear un usuario sin token")


public void crearUsuarioSinToken() {

SerenityRest.given().body(payload.randomBodyRequestPostUser());
}
}

Genere la clase “EliminarUsuarioStepDef” en el paquete src.test.java.cucumber.usuario.stepDef


con el siguiente código:

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;

public class EliminarUsuarioStepDef {


@Steps
UsuarioAPI usuarioAPI;

@Given("eliminar usuario con ID valido")


public void eliminarUsuarioConIDValido() {
Response response =
SerenityRest.given().get(usuarioAPI.GET_USER);
JsonPath jsonPathEvaluator = response.jsonPath();

usuarioAPI.eliminarUsuario(jsonPathEvaluator.get("[0].id").toStri
ng(), true);
}

@When("enviar solicitud eliminar usuario")


public void enviarSolicitudEliminarUsuario() {
SerenityRest.when().delete(usuarioAPI.DELETE_USER);
}

@Given("eliminar usuario con ID {string}")


public void eliminarUsuarioConID(String id) {
usuarioAPI.eliminarUsuario(id, true);
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 113

Genere el archivo “actualizar_usuario.feature” en el paquete src.test.resources.features.usuario


con el siguiente código:

@Test
Feature: Actualizar usuario

Scenario: Actualizar usuario con ID valido y con request body


valido
Given actualizar usuario con ID valido and con request body
valido
When enviar solicitud actualizar usuario
Then debería devolver el codigo 200
And validar usuario json schema

Scenario: Actualizar usuario con ID no registrado y request


body valido
Given actualizar usuario con "1" como ID y "valid" request
body
When enviar solicitud actualizar usuario
Then debería devolver el codigo 404

Scenario: Actualizar usuario sin token


Given actualizar usuario sin token
When enviar solicitud actualizar usuario
Then debería devolver el codigo 401
And mensaje del response body message deberia ser
"Authentication failed"
And validar json schema response error

Scenario: Actualizar usuario sin ID


Given actualizar usuario con "" como ID y "valid" request
body
When enviar solicitud actualizar usuario
Then debería devolver el codigo 404

Genere el archivo “crear_usuario.feature” en el paquete src.test.resources.features.usuario con


el siguiente código:

@Test
Feature: Crear usuario

Scenario: Crear usuario con request body valido


Given deseo crear un usuario con request body valido
When envio request para crear usuario
Then debería devolver el codigo 201
And validar json schema crear usuario

Scenario: Crear usuario sin token


Given deseo crear un usuario sin token
When envio request para crear usuario
Then debería devolver el codigo 401
And mensaje del response body message deberia ser
"Authentication failed"
And validar json schema response error

Scenario: Crear usuario sin token y sin request body


When envio request para crear usuario
Then debería devolver el codigo 401
And mensaje del response body message deberia ser

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 114

"Authentication failed"
And validar json schema response error

Genere el archivo “eliminar_usuario.feature” en el paquete src.test.resources.features.usuario


con el siguiente código:

@Test
Feature: Eliminar usuario

Scenario: Eliminar usuario con ID valido


Given eliminar usuario con ID valido
When enviar solicitud eliminar usuario
Then debería devolver el codigo 204

Scenario: Eliminar usuario con ID no registrada


Given eliminar usuario con ID "1"
When enviar solicitud eliminar usuario
Then debería devolver el codigo 404
And mensaje del response body message deberia ser "Resource
not found"

Scenario: Eliminar usuario sin ID


Given eliminar usuario con ID ""
When enviar solicitud eliminar usuario
Then debería devolver el codigo 404

Genere el archivo “ResponseCreateUserSchemaValid.json” en el paquete


src.test.resources.JSON.Schema con el siguiente código:

{
"$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"
]
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 115

Genere el archivo “ResponseUpdateUserSchemaValid.json” en el paquete


src.test.resources.JSON.Schema con el siguiente código:

{
"$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"
]
}

Genere el archivo “InvalidRequestUpdateUser.json” en el paquete


src.test.resources.JSON.Request con el siguiente código:

{
"user_name_invalid": "update"
}

Genere el archivo “ValidRequestUpdateUser.json” en el paquete


src.test.resources.JSON.Request con el siguiente código:
{
"nam": "Tenali",
"email": "tenali.ramakrishnaa1@16ce.com",
"status": "active"
}

Ejecutar el programa CucumberTestSuite:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 116

Figura 2.16: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

Revisar en la carpeta target/site/serenity la creación de varios archivos .html con el resultado


de la ejecución:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 117

Figura 2.17: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 118

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.

2. El objetivo es escribir criterios de aceptación automatizados y documentación viva para


sus API REST utilizando la poderosa combinación de Cucumber, Rest-Assured y Serenity
BDD con el patrón Screenplay.

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

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 119

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

3.1 Tema 5 : Pruebas funcionales con Selenium


3.1.1 : Selenium IDE
3.1.2 : Configuración de Selenium
3.1.3 : Integrar script de Selenium con IntelliJ

3.2 Tema 6 : Tema 6: Pruebas Funcionales web utilizando Selenium y lenguaje


Gherkin integrado con Cucumber y Serenity
3.2.1 : Creación y Ejecución de una prueba funcional web simple utilizando
Selenium y lenguaje Gherkin integrado con Cucumber y Serenity
3.2.2 : Creación y Ejecución de una prueba funcional web de regular complejidad
utilizando Selenium y lenguaje Gherkin integrado con Cucumber y
Serenity
3.2.3 : Creación y Ejecución de una prueba funcional web avanzada utilizando
Selenium y lenguaje Gherkin integrado con Cucumber y Serenity

ACTIVIDADES PROPUESTAS

• Los alumnos interiorizan cómo identificar la terminología de automatización de


prueba funcional web.
• Los alumnos crean y ejecutan una aplicación Java utilizando Selenium y lenguaje
Gherkin integrado con Cucumber y Serenity.
• Los alumnos aplican BDD en la elaboración de pruebas funcionales web.

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 120

3.1. PRUEBAS FUNCIONALES CON SELENIUM


3.1.1. Selenium IDE

Selenium es un entorno de pruebas de software para aplicaciones basadas en la web. Selenium


provee una herramienta de grabar/reproducir para crear pruebas sin usar un lenguaje de
scripting para pruebas (Selenium IDE). Incluye también un lenguaje específico de dominio para
pruebas (Selenese) para escribir pruebas en un amplio número de lenguajes de programación
populares incluyendo Java, C#, Ruby, Groovy, Perl, Php y Python (Testeando Software, 2015)

Componentes de Selenium

Selenium IDE: Permite grabar, editar y depurar pruebas.

Selenium Client API: Interfaz de programación de aplicaciones (API) de clientes Como


alternativa a escribir pruebas en Selenese, las pruebas pueden escribirse en varios lenguajes de
programación, éstos se comunican con Selenium mediante llamadas a los métodos de Selenium
Client API.

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.

Selenium Grid: permite ejecutar pruebas en paralelo en múltiples máquinas y manejar


diferentes versiones y configuraciones de manera centralizada (JhonyCode Web Dev, 2017).

Figura 3.1: Componentes de Selenium

Adaptado de testingdocs, por testingdocs Official Blog, s.f.,


https://www.testingdocs.com/questions/what-are-different-selenium-components/

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 121

3.1.2. Configuración de Selenium

Habilitar Selenium IDE en Chrome

En Chrome ingresar a la opción de extensiones:

Figura 3.2: Ingresar a la opción de extensiones

Nota: Elaboración propia

Ingresar el Menú Principal y entrar a la opción “Abrir Chrome Web Store”:

Figura 3.3: Opción de extensiones

Nota: Elaboración propia

Figura 3.4: Abrir Chrome Web Store

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 122

Buscar “Selenium ide” y hacer click en el botón “Añadir a Chrome”:

Figura 3.5: Buscar Selenium IDE

Nota: Elaboración propia

Aparecerá el mensaje de confirmación, presionar el botón “Añadir extensión”:

Figura 3.6: Añadir extensión

Nota: Elaboración propia

Saldrá mensaje de extensión añadida:

Figura 3.7: Selenium IDE añadido

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 123

Habilitar Selenium IDE en Firefox

En Firefox ingresar a la opción de complementos:

Figura 3.8: Añadir Selenium IDE en Firefox

Nota: Elaboración propia

Buscar “Selenium ide”:

Figura 3.9: Buscar Selenium IDE

Nota: Elaboración propia

Seleccionar “Selenium IDE”:

Figura 3.10: Buscar Selenium IDE

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 124

Seleccionar “Agregar a Firefox”:

Figura 3.11: Agregar complemento Selenium IDE

Nota: Elaboración propia

Seleccionar “Añadir”:

Figura 3.12: Añadir complemento Selenium IDE

Nota: Elaboración propia

Aparecerá el siguiente mensaje:

Figura 3.13: Complemento Selenium IDE añadido

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 125

Habilitar Selenium IDE en Microsoft Edge

En Edge ingresar a la opción de complementos:

Figura 3.14: Agregar extensión Selenium IDE

Nota: Elaboración propia

Seleccionar la opción “Administrar extensiones”:

Figura 3.15: Administrar extensiones

Nota: Elaboración propia

Ir a la opción “Obtener extensiones para Microsoft Edge”:

Figura 3.16: Obtener extensiones

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 126

Buscar “Selenium” y obtener la extensión “Selenium IDE”:

Figura 3.17: Buscar Selenium

Nota: Elaboración propia

Seleccionar “Agregar extensión”:

Figura 3.18: Agregar extensión

Nota: Elaboración propia

Aparecerá el siguiente mensaje:

Figura 3.19: Extensión agregada

Nota: Elaboración propia

Instalar Selenium Side Runner

Pasos para descargar Selenium Side Runner:


https://www.selenium.dev/selenium-ide/docs/en/introduction/command-line-runner

Descargar el instalador del Node.js de la página:


https://nodejs.org/es/download/

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 127

Figura 3.20: Instalar node

Nota: Elaboración propia

Instalarlo y verificar la instalación del Node.js con el comando: node -v:

Figura 3.21: Versión de node

Nota: Elaboración propia

Instalar Selenium Side Runner con el comando: npm install -g selenium-side-runner

Figura 3.22: Instalación de Selenium Side Runner

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 128

Verificar instalación de Selenium Side Runner: selenium-side-runner –version

Figura 3.23: Verificar instalación de Selenium Side Runner

Nota: Elaboración propia

Instalar el driver de Chrome con el comando: npm install -g chromedriver

Figura 3.24: Instalar driver de Chrome para Selenium

Nota: Elaboración propia

Aparecerá el driver chromedriver en la siguiente ruta:

Figura 3.25: Ruta donde se instala el driver de Chrome para Selenium

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 129

Agregar la ruta del chromedriver en Variables de entorno:

Figura 3.26: Agregar variable de entorno del driver de Chrome para Selenium

Nota: Elaboración propia

Instalar el driver de Firefox con el comando: npm install -g geckodriver

Figura 3.27: Instalar driver de Firefox para Selenium

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 130

En caso no aparezca el archivo “geckodriver.exe” en la carpeta geckodriver en la siguiente ruta:

Figura 3.28: Ruta del driver de Firefox para Selenium

Nota: Elaboración propia

Descargar el driver de la siguiente ruta (cotejar según la versión de tu brower):


https://github.com/mozilla/geckodriver/releases

Figura 3.29: Descargar driver de Firefox para Selenium

Nota: Elaboración propia

El zip descargado descomprimirlo en la ruta del geckodriver:

Figura 3.30: Descomprimir driver de Firefox para Selenium

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 131

Agregar la ruta del geckodriver en Variables de entorno:

Figura 3.31: Agregar ruta del driver de Firefox en variables de entorno

Nota: Elaboración propia

Instalar el driver de Edge con el comando: npm install -g edgedriver

Figura 3.32: Instalar driver de Edge para Selenium

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 132

Aparecerá la carpeta edgedriver en la siguiente ruta:

Figura 3.33: Ruta del driver de Edge para Selenium

Nota: Elaboración propia

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/

Figura 3.34: Descargar driver de Edge para Selenium

Nota: Elaboración propia

Descomprimir el zip descargado en la ruta donde se generó la carpeta edgedriver:

Figura 3.35: Descomprimir driver de Edge para Selenium

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 133

Agregar la ruta del edgedriver en Variables de entorno:

Figura 3.36: Ruta del driver de Edge para Selenium

Nota: Elaboración propia

3.1.3. Integrar script de Selenium con IntelliJ

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.

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 134

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.

2. Selenium es una de las mejores herramientas para la ejecución de pruebas funcionales


web.

3. Selenium permite la automatización de pruebas funcionales web.

4. Con Selenium se pueden realizar pruebas de end to end.

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

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 135

3.2. PRUEBAS FUNCIONALES WEB UTILIZANDO SELENIUM Y LENGUAJE


GHERKIN INTEGRADO CON CUCUMBER Y SERENITY
3.2.1. Creación y Ejecución de una prueba funcional web simple utilizando Selenium
y lenguaje Gherkin integrado con Cucumber y Serenity

Implemente las pruebas funcionales web con Selenium para realizar el logueo de la página de
demo: https://www.saucedemo.com/

Abrimos Chrome y hacemos click en el ícono de Selenium IDE:

Figura 3.37: Iniciar grabación de proyecto con Selenium

Nota: Elaboración propia

Se abrirá la siguiente pantalla, luego seleccionar la opción “Record a new test in a new project”:

Figura 3.38: Nuevo proyecto con Selenium

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 136

Colocamos de nombre: LoginDemoSauce y presionamos OK:

Figura 3.39: Grabación de LoginDemoSauce

Nota: Elaboración propia

Agregamos la url de saucedemo: https://www.saucedemo.com/ y presionamos “START


RECORDING”:

Figura 3.40: Ruta de Saucedemo

Nota: Elaboración propia

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…”:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 137

Figura 3.41: Detener grabación

Nota: Elaboración propia

Luego, presionamos el botón de stop:

Figura 3.42: Presionar botón Stop

Nota: Elaboración propia

Va a pedir un nombre para guardar el test, colocamos PruebaLogin y luego presionamos OK:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 138

Figura 3.43: Ingresando nombre del test

Nota: Elaboración propia

Probamos el proyecto, para ello lo ejecutamos con la opción “Run current test”:

Figura 3.44: Run current test

Nota: Elaboración propia

Observamos que se ejecuta el proyecto de forma exitosa:

Figura 3.45: Se ejecuta el proyecto de forma exitosa

Nota: Elaboración propia

Guardamos el proyecto para poder volver a cargarlo cuando queramos con la opción “Save
project”:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 139

Figura 3.46: Guardando proyecto

Nota: Elaboración propia

Por defecto se graba con el nombre del proyecto y con la extensión .side, en este caso
“LoginDemoSauce”:

Figura 3.47: Proyecto extensión .side

Nota: Elaboración propia

Luego, exportamos nuestro código con click derecho sobre el nombre del test:

Figura 3.48: Exportar el código

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 140

Seleccionamos exportar en “Java JUnit”, presionamos EXPORT:

Figura 3.49: Exportar en JUnit

Nota: Elaboración propia

Se exporta el código en .java:

Figura 3.50: Exportar código en .java

Nota: Elaboración propia

Luego, creamos un proyecto llamado “Tema6Ejercicio1” sin agregar dependencias.

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:

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 141

<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:

package pe.edu.cibertec.Tema6Ejercicio1;// Generated by Selenium


IDE

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;

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 142

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import java.util.*;

public class PruebaLoginTest {


private static WebDriver driver;
private static Map<String, Object> vars;
private static JavascriptExecutor js;
@BeforeAll
public static void setUp() {
driver = new ChromeDriver();
js = (JavascriptExecutor) driver;
vars = new HashMap<String, Object>();
}
@AfterAll
public static void tearDown() {
driver.quit();
}
@Test
public void pruebaLogin() {
driver.get("https://www.saucedemo.com/");
driver.manage().window().setSize(new Dimension(1936, 1056));
driver.findElement(By.cssSelector("*[data-
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();
}
}

Al ejecutar la clase PruebaLoginTest.java cargará rápidamente la prueba del login generado en


Chrome (como si se tratase de una macro) y el test “pruebaLogin()” debe mostrarse como
ejecución exitosa.
Figura 3.51: Ejecución de la clase PruebaLoginTest.java

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 143

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.

3.2.2. Creación y Ejecución de una prueba funcional web de regular complejidad


utilizando Selenium y lenguaje Gherkin integrado con Cucumber y Serenity

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/

Creamos un proyecto llamado “Tema6Ejercicio2” sin agregar dependencias.

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:

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pe.edu.cibertec</groupId>
<artifactId>Tema6Ejercicio2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Tema6Ejercicio2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>20</java.version>
<logback.version>1.4.7</logback.version>
<serenity.version>2.6.0</serenity.version>

<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>

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 144

</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>

<!-- Librerias generales de testing -->


<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

Genere los siguientes paquetes en el paquete src.test:

- java.cucumber.stepdefs
- java.cucumber.pages
- resources.features.login

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 145

Genere la clase “Pagina” en el paquete src.test.java.cucumber.pages con el siguiente código:

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");

public Pagina(WebDriver driver) {


this.driver = driver;
}

/**
* 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;
}

/**

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 146

* Method for checking if the page title is correct by


passing in the title that the page should have
*/
public Boolean isPageTitleCorrect(String title) {
try {
return
driver.findElement(pageTitle).getText().equals(title);
} 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) {

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 147

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 {

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 148

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;
}
}

Genere la clase “PaginaLogin” en el paquete src.test.java.cucumber.pages con el siguiente


código:

package cucumber.pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

/**

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 149

* A class that represents the log in page in the Sauce Demo


website
*/
public class PaginaLogin extends Pagina {

// 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");

public PaginaLogin(WebDriver webDriver) {


super(webDriver);
this.getDriver().get("https://www.saucedemo.com/");
}

/**
* 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();
}
}
}

Genere la clase “PaginaInventario” en el paquete src.test.java.cucumber.pages con el siguiente


código:

package cucumber.pages;

import org.openqa.selenium.By;

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 150

import org.openqa.selenium.WebDriver;

/**
* A class that represents the inventory page in the Sauce Demo
website
*/
public class PaginaInventario extends Pagina {

// Accessing the web elements from the inventory page that


could be useful for the testers
private By itemImages = new
By.ByClassName("inventory_item_img");
private By itemNames = new
By.ByClassName("inventory_item_name");
private By itemPrices = new
By.ByClassName("inventory_item_price");
private By itemDescription = new
By.ByClassName("inventory_item_desc");
private By addToCartOrRemoveButtons = new
By.ByClassName("btn");
private By filterButton = new
By.ByClassName("product_sort_container");
private By filterOptions = new By.ByTagName("option");
private By shoppingCartBade = new
By.ByClassName("shopping_cart_badge");

public PaginaInventario(WebDriver webDriver) {


super(webDriver);

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;
}

/**

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 151

* Method for clicking any item in inventory page by passing


in its index
*/
public void clickItemNameAtIndex(int index) {
try {

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 {

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 152

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();
}
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 153

/**
* 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;
}
}

Genere la clase “LoginStepDefs” en el paquete src.test.java.cucumber.stepdefs con el siguiente


código:

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;

public class LoginStepDefs {

private static WebDriver driver;


private PaginaLogin paginaLogin;
private PaginaInventario paginaInventario;

@Given("Estoy en la página de inicio de sesion")


public void estoyEnLaPaginaInicio() {
driver = new ChromeDriver();
driver.get("https://www.saucedemo.com");
paginaLogin = new PaginaLogin(driver);
}

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 154

@When("Ingreso {string} y password secret_sauce")


public void ingresoUsernameYPassword(String username) {
paginaLogin.ingresarUsername(username);
paginaLogin.ingresarPassword("secret_sauce");
}

@And("Hago clic en el boton de inicio de sesion")


public void clickBotonInicioSesion() {
paginaLogin.clickBotonLogin();
}

@Then("Ingresare a la pagina de inventario")


public void ingresarePaginaInventario() {
driver.manage().timeouts().pageLoadTimeout(10,
TimeUnit.SECONDS);
paginaInventario = new PaginaInventario(driver);
}

@When("Inserto un username invalido y un password valido")


public void iInsertAnInvalidUsername() {
paginaLogin.ingresarUsername("locked_out_user");
paginaLogin.ingresarPassword("secret_sauce");
}

@When("Inserto un username valido y un password invalido")


public void iInsertAnInvalidPassword() {
paginaLogin.ingresarUsername("standard_user");
paginaLogin.ingresarPassword("12345");
}

@Then("Me quedare en la pagina de inicio de sesion")


public void meQuedareEnPaginaInicioSesion() {
Assertions.assertTrue(
"https://www.saucedemo.com".equals(driver.getCurrentUrl()) ||
"https://www.saucedemo.com/".equals(driver.getCurrentUrl()));
}

@After
public void tearDown(){
if(driver != null){
driver.quit();
System.out.println("tearDown");
}
}
}

Genere la clase “CucumberTestSuite” en el paquete src.test.java.cucumber con el siguiente


código:

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 {}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 155

Genere el archivo “login.feature” en el paquete src.test.resources.features.login con el siguiente


código:

Feature: Verificar las credenciales y navegar a diferentes


páginas en el sitio web saucedemo

Background: Configuracion del navegador


Given Estoy en la página de inicio de sesion

Scenario Outline: Llegar a la pagina de inventario desde la


pagina de inicio de sesion
When Ingreso <username> y password secret_sauce
And Hago clic en el boton de inicio de sesion
Then Ingresare a la pagina de inventario

Examples:
| username |
|"standard_user" |
|"problem_user" |
|"performance_glitch_user"|

Scenario: Permanecer en la pagina de inicio de sesion como


resultado de username incorrecto
When Inserto un username invalido y un password valido
And Hago clic en el boton de inicio de sesion
Then Me quedare en la pagina de inicio de sesion

Scenario: Permanecer en la pagina de inicio de sesion como


resultado de password incorrecto
When Inserto un username valido y un password invalido
And Hago clic en el boton de inicio de sesion
Then Me quedare en la pagina de inicio de sesion

Genere el archivo “logback-test.xml” en el paquete src.test.resources con el siguiente código:

<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>

<logger name="root" level="DEBUG"/>


<logger name="net.serenitybdd" level="WARN"/>
<logger name="net.thucydides" level="WARN"/>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>

</configuration>

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 156

Ejecutar el programa CucumberTestSuite:

Figura 3.52: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

Revisar en la carpeta target/site/serenity la creación de 3 archivos .html con el resultado de la


ejecución:

Figura 3.53: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 157

3.2.3. Creación y Ejecución de una prueba funcional web avanzada utilizando


Selenium y lenguaje Gherkin integrado con Cucumber y Serenity

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”:

Empezamos la grabación desde el login, una vez dentro de la plataforma, seleccionamos un


producto y lo enviamos al carrito de compras, luego procedemos a terminar la venta (ingresando
los datos que nos piden) y por último presionamos en el botón de “Back Home”, por último,
detenemos la grabación presionando de nuevo el icono de Selenium:

Figura 3.54: Detener grabación

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 158

Luego, presionamos el botón de stop:

Figura 3.55: Presionar botón Stop

Nota: Elaboración propia

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:

Figura 3.56: Se ejecuta el proyecto de forma exitosa

Nota: Elaboración propia

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:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 159

Figura 3.57: Exportar el código

Nota: Elaboración propia

Seleccionamos exportar en “Java JUnit”, presionamos EXPORT, se exporta el código en .java.


Luego, creamos un proyecto llamado “Tema6Ejercicio3” sin agregar dependencias.

Elimine la clase Tema6Ejercicio3ApplicationTests.java y en su lugar copie el archivo


PruebaCompraTest.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:

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>pe.edu.cibertec</groupId>
<artifactId>Tema6Ejercicio3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Tema6Ejercicio3</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>

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 160

<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:

package pe.edu.cibertec.Tema6Ejercicio3;// Generated by Selenium


IDE

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.*;

public class PruebaCompraTest {


private static WebDriver driver;
private static Map<String, Object> vars;
private static JavascriptExecutor js;
@BeforeAll
public static void setUp() {
driver = new ChromeDriver();
js = (JavascriptExecutor) driver;
vars = new HashMap<String, Object>();
}
@AfterAll
public static void tearDown() {
driver.quit();
}
@Test
public void pruebaCompra() {
driver.get("https://www.saucedemo.com/");
driver.manage().window().setSize(new Dimension(1936, 1056));
driver.findElement(By.cssSelector("*[data-

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 161

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();
}
}

Al ejecutar la clase PruebaCompraTest.java cargará rápidamente la prueba del login generado


en Chrome (como si se tratase de una macro) y el test “pruebaCompra()” debe mostrarse como
ejecución exitosa.

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 162

Figura 3.58: Ejecución de la clase PruebaCompraTest.java

Nota: Elaboración propia

Homologamos este proyecto con el código implementado en el proyecto “Tema6Ejercicio2”,


homologando las carpetas “src.test.java.cucumber”, “src.test.resources” y el archivo “pom.xml”

Agregamos dependencias en el archivo “pom.xml” para que quede el siguiente código:

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pe.edu.cibertec</groupId>
<artifactId>Tema6Ejercicio3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Tema6Ejercicio3</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>20</java.version>
<logback.version>1.4.7</logback.version>
<serenity.version>2.6.0</serenity.version>

<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>

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 163

<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>

<!-- Librerias generales de testing -->


<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

Genere la clase “PaginaInventarioItem” en el paquete src.test.java.cucumber.pages con el


siguiente código:

package cucumber.pages;

import org.openqa.selenium.By;

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 164

import org.openqa.selenium.WebDriver;

/**
* A class that represents the inventory item page in the Sauce
Demo website
*/
public class PaginaInventarioItem extends Pagina {

// Accessing the web elements from the inventory item page


that could be useful for the testers
private By itemImage = new
By.ByClassName("inventory_details_img");
private By itemName = new
By.ByClassName("inventory_details_name");
private By itemDescription = new
By.ByClassName("inventory_details_desc");
private By itemPrice = new
By.ByClassName("inventory_details_price");
private By backToProductsButton = new By.ById("back-to-
products");
private By inventoryPageButtons = new By.ByClassName("btn");
private By shoppingCartBade = new
By.ByClassName("shopping_cart_badge");

public PaginaInventarioItem(WebDriver webDriver) {


super(webDriver);
}

/**
* 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() {

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 165

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();

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 166

}
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;
}
}

Genere la clase “PaginaCarrito” en el paquete src.test.java.cucumber.pages con el siguiente


código:

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");

public PaginaCarrito(WebDriver webDriver) {


super(webDriver);

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();

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 167

} 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;

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 168

/**
* 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();
}
}
}

Genere la clase “PaginaPagar” en el paquete src.test.java.cucumber.pages con el siguiente


código:

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 {

// Accessing the web elements from the checkout stage one


page that could be useful for the testers
private By firstNameTextBox = new By.ById(“first-name”);
private By lastNameTextBox = new By.ById(“last-name”);
private By zipCodeTextBox = new By.ById(“postal-code”);
private By cancelButton = new By.ById(“cancel”);
private By continueButton = new By.ById(“continue”);

public PaginaPagar(WebDriver webDriver) {


super(webDriver);
this.getDriver().get(“https://www.saucedemo.com/checkout-
step-one.html”);
}

/**
* Method for entering a string of choice into the first name
text box
*/
public void enterFirstNameInTextBox(String firstName) {
try {

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 169

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();
}

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 170

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();
}
}
}

Genere la clase “CarritoStepDefs” en el paquete src.test.java.cucumber.stepdefs con el siguiente


Código:

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;

public class CarritoStepDefs {


private static WebDriver driver;
private PaginaCarrito paginaCarrito;
private PaginaLogin paginaLogin;
private PaginaInventario paginaInventario;
private PaginaInventarioItem paginaInventarioItem;

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 171

private PaginaPagar paginaPagar;

/*
@Before
public void setup() {
System.setProperty("webdriver.chrome.driver",
"src/test/resources/chromedriver.exe");
System.out.println("setup");
}*/

@Given("Estoy en la pagina del carrito")


public void estoyEnLaPaginaCarrito() {
driver = new ChromeDriver();
driver.get("https://www.saucedemo.com");

paginaLogin = new PaginaLogin(driver);


paginaLogin.ingresarUsername("standard_user");
paginaLogin.ingresarPassword("secret_sauce");
paginaLogin.clickBotonLogin();
paginaInventario = new PaginaInventario(driver);
paginaInventario.clickAddToCartOrRemoveButtonAtIndex(0);
paginaInventario.clickAddToCartOrRemoveButtonAtIndex(1);
paginaInventario.clickAddToCartOrRemoveButtonAtIndex(2);
paginaInventario.clickCartIcon();
}

@When("Hago clic en el titulo del producto")


public void clicEnTituloDelProducto() {
paginaCarrito = new PaginaCarrito(driver);
paginaCarrito.clickItemAtIndex(2);
}

@Then("Ire a la pagina de articulos de inventario de ese


producto")
public void irePaginaArticulosInventario() {
paginaInventarioItem = new PaginaInventarioItem(driver);

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();

@Then("Volvere a la pagina de inventario")


public void volverePaginaInventario() {

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);

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 172

paginaCarrito.clickCheckoutButton();
}

@Then("Ire a la pagina 'checkout step one'")


public void irePaginaPagar() {
paginaPagar = new PaginaPagar(driver);

Assertions.assertEquals("https://www.saucedemo.com/checkout-step-
one.html", paginaPagar.getCurrentURL());
}

@When("Un articulo se muestra dentro del carrito")


public void seMuestraArticuloEnCarrito() {
paginaCarrito = new PaginaCarrito(driver);
paginaCarrito.getItemNameAtIndex(2);
}

@Then("Tendre una opcion para eliminar ese articulo del


carrito")
public void eliminarArticuloDelCarrito() {
paginaCarrito.clickRemoveItemAtIndex(2);
Assertions.assertEquals(2,paginaCarrito.getCartSize());
}

@When("Un articulo se muestra en la pagina del carrito")


public void articuloSeMuestraPaginaCarrito() {
paginaCarrito = new PaginaCarrito(driver);
paginaCarrito.getItemPriceAtIndex(0);
}

@Then("Vere el precio del producto")


public void verePrecioProducto() {
Assertions.assertEquals("$29.99",
paginaCarrito.getItemPriceAtIndex(0));
}

@After
public void tearDown(){
if(driver != null){
driver.quit();
System.out.println("tearDown");
}
}
}

Crear el paquete src.test.resources.features.carrito y genere el archivo “carrito.feature” dentro


del mismo con el siguiente código:

Feature: Navegando a traves de la pagina del carrito

Background: Usando la pagina del carrito


Given Estoy en la pagina del carrito

Scenario: Haciendo clic en el titulo del producto


When Hago clic en el titulo del producto
Then Ire a la pagina de articulos de inventario de ese
producto

Scenario: Clicking on 'continue shopping' in cart page


When Hago clic en 'continue shopping' en la pagina del

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 173

carrito
Then Volvere a la pagina de inventario

Scenario: Al hacer clic en 'Checkout' en la pagina del carrito


When Hago clic en 'Checkout' en la pagina del carrito
Then Ire a la pagina 'checkout step one'

Scenario: Se muestra un articulo en el carrito y tengo opcion


de eliminarlo
When Un articulo se muestra dentro del carrito
Then Tendre una opcion para eliminar ese articulo del carrito

Scenario: Se muestra precio de articulo en el carrito


When Un articulo se muestra en la pagina del carrito
Then Vere el precio del producto

Ejecutar el programa CucumberTestSuite:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 174

Figura 3.59: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

Revisar en la carpeta target/site/serenity la creación de 7 archivos .html con el resultado de la


ejecución:

Figura 3.60: Resultado ejecución programa CucumberTestSuite

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 175

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.

3. Las pruebas funcionales son a partir de una interfaz gráfica.

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

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 176

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

• Los alumnos interiorizan cómo identificar la terminología de automatización de


prueba funcional para móviles.
• Los alumnos crean y ejecutan una aplicación Java utilizando Appium y lenguaje
Gherkin integrado con Cucumber y Serenity.
• Los alumnos aplican BDD en la elaboración de pruebas funcionales móviles.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 177

4.1. PRUEBAS FUNCIONALES CON APPIUM


4.1.1. Appium

Appium es un framework de automatización de pruebas de código abierto que impulsan


aplicaciones nativas, webs e híbridas tanto para iOS y Android utilizando el protocolo WebDriver,
es decir, la API de Selenium. Appium está basado en Selenium y se usa para probar aplicaciones
móviles (Club de Tecnología, 2023)

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.)

Figura 4.1: Flujo de Appium

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/

Descargar e instalar el servidor Appium de la siguiente ruta:

https://github.com/appium/appium-desktop/releases/tag/v1.22.3-4

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 178

Figura 4.2: Descargar Appium Destop

Nota: Elaboración propia

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:

 Comprobar el DOM de la página de la aplicación que estamos viendo.


 Comprobar los identificadores únicos de cada elemento.
 Realizar acciones sobre los elementos que hemos encontrado con sus
identificadores: comprobar si está visible, hacer click, tap, etc. (ESTEFAFDE,
2019).

Descargar e instalar el Appium Inspector de la siguiente ruta:


https://github.com/appium/appium-inspector/releases

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 179

Figura 4.3: Descargar Appium Destop

Nota: Elaboración propia

Tipos de Aplicaciones Móviles:

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

Descargar Android Studio de la siguiente ruta:


https://developer.android.com/studio

Luego de instalar Android Studio agregar el SDK de Android a las variables de entorno
ANDROID_HOME:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 180

Figura 4.4: Configurar variables de entorno

Nota: Elaboración propia

Asegurarse de tener configurado la variable de entorno JAVA_HOME:

Figura 4.5: Configurar variables de entorno

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 181

Android Debug Bridge (ADB)

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.

También permite reiniciar un dispositivo Android de forma estándar, en modo recovery


(recuperación), bootloader (cargados de arranque) o realizando un proceso previo de
sideloading (transferir ficheros entre dos dispositivos locales, cosa que se suele hacer cuando se
quiere cambiar el Android del fabricante por un ROM personalizado), además de permitir llevar
a cabo otras muchas acciones (Medina, 2022).

Descargar el ejecutable de ADB (Android Debug Bridge) de la siguiente ruta y descomprimirlo:


https://dl.google.com/android/repository/platform-tools-latest-windows.zip

4.1.2. Generación de script con Appium

Abrir “Android Studio”, generar un nuevo proyecto seleccionando “Basic Activity”:

Figura 4.6: Nuevo proyecto Basic Activity

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 182

Colocar los datos del proyecto y presionar botón “Finish”:

Figura 4.7: Nuevo proyecto

Nota: Elaboración propia


Se generará un proyecto nuevo:
Figura 4.8: Nuevo proyecto

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 183

Cambiar la vista “Android” a la vista “Project”:

Figura 4.9: Vista Project

Nota: Elaboración propia

Generar un emulador en Android, para ello seleccionar AVD Manager de la parte superior del
proyecto:
Figura 4.10: Vista Project

Nota: Elaboración propia

Crear un Virtual Device:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 184

Figura 4.11: Crear Virtual Device

Nota: Elaboración propia

Seleccionar una opción (por ejemplo, Pixel 2):

Figura 4.12: Seleccionar opción Pixel 2

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 185

Descargar la última versión API Level 33:

Figura 4.13: Descargar API 33

Nota: Elaboración propia

Aceptar:
Figura 4.14: Aceptar

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 186

Esperar la instalación:

Figura 4.15: Esperar instalación

Nota: Elaboración propia

Finalizar:

Figura 4.16: Finalizar instalación

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 187

Seleccionar “Next” seleccionando la versión 33:

Figura 4.17: Seleccionar API 33

Nota: Elaboración propia

Seleccionar “Finish”:

Figura 4.18: Finish

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 188

Iniciar el emulador:

Figura 4.19: Iniciar emulador

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 189

Aparecerá el emulador:

Figura 4.20: Iniciar emulador

Nota: Elaboración propia

Nos posicionamos en la ruta donde se descomprimió el ADB y abrimos un CMD:

Figura 4.21: Abriendo ADB

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 190

Clic derecho sobre el terminar y seleccionar propiedades, cambiar el tamaño del búfer a 999 y
luego “Aceptar”:

Figura 4.22: Propiedades CMD

Nota: Elaboración propia

En CMD colocar el comando “adb devices”, luego enter (copiemos el nombre del emulador
“emulator-5554”). Luego colocar el comando “adb logcat”:

Figura 4.23: Propiedades CMD

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 191

Al presionar enter, se va a visualizar todo el log del emulador (no cerrar el CMD):

Figura 4.24: Logs del emulador

Nota: Elaboración propia

Regresar al emulador e instalar el apk compartido llamado “E Commerce Android App


Template_v1.0_apkgk.com.apk”, para ello simplemente arrastrar el apk sobre el emulador,
aparecerá de la siguiente forma:

Figura 4.25: Instalando emulador

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 192

Abrimos el apk instalado:

Figura 4.26: Abriendo APK

Nota: Elaboración propia

Inmediatamente, regresamos al CMD y colocamos CTRL + C para detener el ADB:

Figura 4.27: Logs del emulador

Nota: Elaboración propia

Luego, copiamos todo el contenido del CMD haciendo clic derecho, Editar, Seleccionar todo:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 193

Figura 4.28: Logs del emulador

Nota: Elaboración propia

Se observará que se seleccionó todo el contenido:

Figura 4.29: Seleccionar todo

Nota: Elaboración propia

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:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 194

Figura 4.30: Pegando contenido en editor de texto

Nota: Elaboración propia

En el editor de texto buscamos la palabra “ActivityTaskManager”, se buscan los últimos


ActivityTaskManager del texto copiado, el ActivityTaskManager nos dará los parámetros para
levantar el aplicativo en el Appium Inspector. Al buscar encontramos los siguientes
ActivityTaskManager:

Figura 4.31: ActivityTaskManager

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 195

Figura 4.32: ActivityTaskManager

Nota: Elaboración propia

Copiando lo obtenido tenemos dos ActivityTaskManager hallados:


 ActivityTaskManager: START u0
{cmp=com.ecommerce.template/.activity.LoginActivity} from uid 10171
 ActivityTaskManager: Displayed
com.ecommerce.template/.activity.SplashScreenActivity: +392ms

Regresamos al emulador y cerrar el apk, estar seguros de haber cerrado la aplicación:

Figura 4.33: Cerrar apk

Nota: Elaboración propia

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:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 196

Figura 4.34: Appium Server GUI

Nota: Elaboración propia

Observamos que el servidor de Appium está ejecutándose:

Figura 4.35: Appium Server ejecutándose

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 197

Iniciamos el Appium Inspector:


Figura 4.36: Appium Inspector

Nota: Elaboración propia

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”

Figura 4.37: LoginActivity

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 198

Observamos que comienza a cargar:

Figura 4.38: Iniciar LoginActivity

Nota: Elaboración propia

Aparecerá un error, con eso descartamos que LoginActivity carga la aplicación:

Figura 4.39: Iniciar LoginActivity

Nota: Elaboración propia

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”

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 199

Figura 4.40: SplashScreenActivity

Nota: Elaboración propia

Observamos que comienza a cargar:

Figura 4.41: Iniciar SplashScreenActivity

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 200

Observamos que esta configuración cargó la aplicación en el Appium Inspector:

Figura 4.42: Iniciar LoginActivity

Nota: Elaboración propia

Asimismo, también se cargó la aplicación en el Emulador, esto es debido a que Appium Inspector
está enviando peticiones al emulador:

Figura 4.43: Iniciar LoginActivity

Nota: Elaboración propia

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:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 201

Figura 4.44: Botón Refresh

Nota: Elaboración propia

Luego de haber presionado el botón Refresh, se sincroniza la vista del Appium Inspector con la
del emulador.

Figura 4.45: Luego de presionar botón Refresh

Nota: Elaboración propia

Ahora vamos a generar un script utilizando el Appium Inspector, para ello hacemos clic en el
botón de grabación:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 202

Figura 4.46: Iniciar grabación de script

Nota: Elaboración propia

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”:

Figura 4.47: Seleccionar campo email

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 203

Figura 4.48: Ingresando email

Nota: Elaboración propia

Se observa que se agrega en el emulador el correo, se genera un código en JUnit y


adicionalmente en el emulador también aparece el correo ingresado:

Figura 4.49: Email ingresado

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 204

Figura 4.50: Email ingresado

Nota: Elaboración propia

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:

Figura 4.51: Password ingresado

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 205

Figura 4.52: Password ingresado

Nota: Elaboración propia

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:

Figura 4.53: Password ingresado

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 206

Figura 4.54: Ingresando al aplicativo

Nota: Elaboración propia

Figura 4.55: Ingresamos a la aplicación

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 207

Figura 4.56: Ingresamos a la aplicación

Nota: Elaboración propia

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:

Figura 4.57: Clic sobre título “E-Commerce App Template”

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 208

Copiamos el código generado en Appium Inspector, con el botón “Copy code to clipboard”:

Figura 4.58: Copiando código

Nota: Elaboración propia

Por último, terminamos la grabación clickeando sobre el botón “Quit Session & Close Inspector”:

Figura 4.59: Quit Session & Close Inspector

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 209

Figura 4.60: Sesión Finalizada

Nota: Elaboración propia

Podemos guardar la configuración con el botón “Save As” y luego botón “Save”:

Figura 4.61: Sesión Finalizada

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 210

Figura 4.62: Guardando sesión

Nota: Elaboración propia

En la sección “Saved Capability Sets” aparecerá la sesión guardada:

Figura 4.63: Sesión guardada

Nota: Elaboración propia

4.1.3. Integrar script de Appium con Android Studio

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.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 211

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.

2. Appium emplea internamente el WebDriver de Selenium.

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

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 212

4.2. 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

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'

Figura 4.64: Agregando dependencias

Nota: Elaboración propia

Luego sincronizamos las librerías agregadas:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 213

Figura 4.65: Agregando dependencias

Nota: Elaboración propia

Luego, en la carpeta “app.src.test.java.pe.edu.cibertec” agregamos la clase


ECommerceLoginUnitTest con el siguiente código (este código es una adaptación del código
generado por el Appium Inspector en la sección “4.1.2. Generación de script con Appium”):

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;

public class ECommerceLoginUnitTest {

private AndroidDriver driver;

@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",

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 214

".activity.SplashScreenActivity");

URL remoteUrl = new URL("http://localhost:4723/wd/hub");

driver = new AndroidDriver(remoteUrl, desiredCapabilities);


}

@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")));

WebElement el1 = (WebElement)


driver.findElement(By.xpath("/hierarchy/android.widget.FrameLayout/and
roid.widget.LinearLayout/android.widget.FrameLayout/android.widget.Lin
earLayout/android.widget.FrameLayout/android.widget.LinearLayout/andro
id.widget.LinearLayout[1]/android.widget.LinearLayout[1]/android.widge
t.FrameLayout/android.widget.EditText"));
el1.sendKeys("christopher@gmail.com");
WebElement el2 = (WebElement)
driver.findElement(By.xpath("/hierarchy/android.widget.FrameLayout/and
roid.widget.LinearLayout/android.widget.FrameLayout/android.widget.Lin
earLayout/android.widget.FrameLayout/android.widget.LinearLayout/andro
id.widget.LinearLayout[1]/android.widget.LinearLayout[2]/android.widge
t.FrameLayout/android.widget.EditText"));
el2.sendKeys("1234");
WebElement el3 = (WebElement)
driver.findElement(By.id("com.ecommerce.template:id/bt_sign_in"));
el3.click();

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();
}
}

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 215

El código de visualizará de la siguiente forma:

Figura 4.66: Código de la clase ECommerceLoginUnitTest

Nota: Elaboración propia

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):

Figura 4.67: Servidor de Appium ejecutándose

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 216

Ejecutamos la prueba unitaria, observaremos que el emulador ejecutará los pasos del login y
terminará de forma exitosa:

Figura 4.68: Ejecución de la clase ECommerceLoginUnitTest

Nota: Elaboración propia

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

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.

Utilizando el mismo apk “E Commerce Android App Template_v1.0_apkgk.com.apk” del


ejercicio anterior, realizamos una grabación del script siguiendo los mismos pasos descriptos en
la sección “4.1.2. Generación de script con Appium” hasta concluir un proceso de compra,
copiamos el código y en la carpeta “app.src.test.java.pe.edu.cibertec” agregamos la clase
ECommerceCompraUnitTest con el siguiente código (utilizamos el mismo proyecto de la sección
4.2.1):

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;

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 217

import io.appium.java_client.android.AndroidDriver;

public class ECommerceCompraUnitTest {

private AndroidDriver driver;

@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");

URL remoteUrl = new URL("http://localhost:4723/wd/hub");

driver = new AndroidDriver(remoteUrl, desiredCapabilities);


}

@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")));

WebElement el1 = (WebElement)


driver.findElement(By.xpath("/hierarchy/android.widget.FrameLayout/and
roid.widget.LinearLayout/android.widget.FrameLayout/android.widget.Lin
earLayout/android.widget.FrameLayout/android.widget.LinearLayout/andro
id.widget.LinearLayout[1]/android.widget.LinearLayout[1]/android.widge
t.FrameLayout/android.widget.EditText"));
el1.sendKeys("christophermen@gmail.com");
WebElement el2 = (WebElement)
driver.findElement(By.xpath("/hierarchy/android.widget.FrameLayout/and
roid.widget.LinearLayout/android.widget.FrameLayout/android.widget.Lin
earLayout/android.widget.FrameLayout/android.widget.LinearLayout/andro
id.widget.LinearLayout[1]/android.widget.LinearLayout[2]/android.widge
t.FrameLayout/android.widget.EditText"));
el2.sendKeys("1234");
WebElement el3 = (WebElement)
driver.findElement(By.id("com.ecommerce.template:id/bt_sign_in"));
el3.click();

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")));

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 218

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.widget.ScrollView/android
.widget.LinearLayout/android.widget.LinearLayout/android.widget.Linear
Layout/androidx.recyclerview.widget.RecyclerView/android.widget.Linear
Layout[1]/android.widget.LinearLayout"));
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();
WebElement el9 = (WebElement)
driver.findElement(By.xpath("/hierarchy/android.widget.FrameLayout/and
roid.widget.LinearLayout/android.widget.FrameLayout/android.widget.Lin
earLayout/android.widget.FrameLayout/android.widget.LinearLayout/andro
id.widget.TextView[1]"));
Assert.assertEquals("Congratulation,your order purchase on the
way",el9.getText());

@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:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 219

Figura 4.69: Ejecución de la clase ECommerceCompraUnitTest

Nota: Elaboración propia

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

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.

Creamos un proyecto llamado “Tema8Ejercicio3” sin agregar dependencias.

Genere los siguientes paquetes en el paquete src.main:


- pe.edu.cibertec.model

Genere los siguientes paquetes en el paquete src.test:


- pe.edu.cibertec.appium
- pe.edu.cibertec.steps
- pe.edu.cibertec.util
- resources.feature

Elimine las clases Tema8Ejercicio3Application.java y Tema8Ejercicio3ApplicationTests.java.

En el archivo pom.xml coloque el siguiente código y recargue Maven:

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 220

<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>

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 221

<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>

En la carpeta src.main.java.pe.edu.cibertec.model genere la clase Usuario con el siguiente


código:
package pe.edu.cibertec.model;

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;
}

En la carpeta src.test.java.pe.edu.cibertec.appium genere la clase BaseAppium con el siguiente


código:
package pe.edu.cibertec.appium;

import io.appium.java_client.android.AndroidDriver;

import org.openqa.selenium.remote.DesiredCapabilities;

import java.net.MalformedURLException;
import java.net.URL;

public class BaseAppium {

public static AndroidDriver driver;

public static void setUp() throws MalformedURLException {


DesiredCapabilities desiredCapabilities = new
DesiredCapabilities();
desiredCapabilities.setCapability("platformName",

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 222

"Android");
desiredCapabilities.setCapability("deviceName",
"emulator-5554");
desiredCapabilities.setCapability("appPackage",
"com.ecommerce.template");
desiredCapabilities.setCapability("appActivity",
".activity.SplashScreenActivity");

URL remoteUrl = new URL("http://localhost:4723/wd/hub");

driver = new AndroidDriver(remoteUrl,


desiredCapabilities);
}

public static void tearDown() {


driver.quit();
}
}

En la carpeta src.test.java.pe.edu.cibertec.appium genere la clase Compra con el siguiente


código:
package pe.edu.cibertec.appium;

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;

public class Compra extends BaseAppium {

public void login(Usuario usuario) {


driver.findElement(By.name("username")).click();

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();
}

public void compra() {


WebDriverWait wait = new WebDriverWait(driver,
Duration.ofSeconds(30));

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")));

WebElement el1 = (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

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 223

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();
}

public boolean validarCompra() {


WebElement el9 = (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.TextView[1]"));
String text = el9.getText();
System.out.println("El valor es: " + text);
if(text.equals("Congratulation,your order purchase on the
way"))
return true;
else
return false;
}
}

En la carpeta src.test.java.pe.edu.cibertec.steps genere la clase CompraSteps con el siguiente


código:
package pe.edu.cibertec.steps;

import io.cucumber.java.After;

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 224

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;

public class CompraSteps {

PropertyLoader loadproperty = new PropertyLoader();


Usuario usuario;

@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());
}
}

@When("hacemos una compra")


public void paso3() {
Compra compra = new Compra();
compra.compra();
}

@Then("la compra es exitosa")


public void paso4() {
Compra compra = new Compra();
Assert.assertTrue(compra.validarCompra());
}

@After
public void close() {
BaseAppium baseAppium = new BaseAppium();
baseAppium.tearDown();
}
}

En la carpeta src.test.java.pe.edu.cibertec.util genere la clase PropertyLoader con el siguiente


código:
package pe.edu.cibertec.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 225

import java.io.IOException;
import java.util.Properties;

public class PropertyLoader {

public String loadProperty(String parameter) {


return System.getProperty(parameter);
}

public Properties loadProperties() {


Properties props = new Properties();
String filePath = new
File("src/test/resources/params.properties").getAbsolutePath();
File file = new File(filePath);
FileInputStream fileInput = null;
try {
fileInput = new FileInputStream(file);
props.load(fileInput);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInput != null) {
try {
fileInput.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return props;
}

En la carpeta src.test.java.pe.edu.cibertec genere la clase CucumberTest con el siguiente código:

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 {
}

En la carpeta src.test.resources.feature genere el archivo compra.feature con el siguiente


código:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 226

Feature: Proceso de compra en el app


Como usuario
Quiero comprar en el app

Background:
Given soy un usuario
And ingreso al app

Scenario: Proceso de compra


When hacemos una compra
Then la compra es exitosa

En la carpeta src.test.resources genere el archivo junit-platform.properties con el siguiente


código:

cucumber.plugin=pretty, json:target/cucumber/cucumber.json,
html:target/cucumber.html

En la carpeta src.test.resources genere el archivo params.properties con el siguiente código:

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:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 227

Figura 4.70: Ejecución de la clase CucumberTest

Nota: Elaboración propia

En la carpeta target se observa la generación de un archivo “cucumber.html” con el resultado


de la ejecución:

Figura 4.71: Archivo cucumber.html

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 228

Resumen
1. Appium es un servidor y se ejecuta en segundo plano.

2. Con un UIAutomator incorporado, Appium es capaz de producir registros de información


detallados y tiene una estructura de informes detallada para un mejor análisis de los
resultados de las pruebas y una depuración mejorada.

3. Ofrece la flexibilidad de escribir código de prueba en diferentes lenguajes (Java, JavaScript,


PHP, Ruby, Python y C#).

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.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 229

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

5.2 Tema 10 : Pruebas de rendimiento con JMeter


5.2.1 : Crear y ejecutar pruebas de rendimiento con JMeter
5.2.2 : Análisis de resultados

ACTIVIDADES PROPUESTAS

• Los alumnos interiorizan el concepto de pruebas de performance.


• Los alumnos crean y reproducen scripts con JMeter.
• Los alumnos tienen la capacidad de analizar los resultados obtenidos con JMeter.

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 230

5.1. INTRODUCCIÓN A LAS PRUEBAS DE PERFORMANCE


5.1.1. Pruebas de performance

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 se utiliza normalmente para romper la aplicación. Se va doblando el número


de usuarios que se agregan a la aplicación y se ejecuta una prueba de carga hasta que
se rompe. Este tipo de prueba se realiza para determinar la solidez de la aplicación en
los momentos de carga extrema y ayuda a los administradores para determinar si la
aplicación rendirá lo suficiente en caso de que la carga real supere a la carga esperada.

Prueba de estabilidad (soak testing)

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.

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 231

Pruebas de picos (spike testing)

La prueba de picos, como el nombre sugiere, trata de observar el comportamiento del


sistema variando el número de usuarios, tanto cuando bajan, como cuando tiene
cambios drásticos en su carga. Esta prueba se recomienda que sea realizada con un
software automatizado que permita realizar cambios en el número de usuarios mientras
que los administradores llevan un registro de los valores a ser monitorizados
(wordpress, s.f.).

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.

A veces se clasifica JMeter como herramienta de "generación de carga", pero esto no es


una descripción completa de la herramienta. JMeter soporta aserciones para asegurar
que los datos recibidos son correctos, por lo que es una herramienta de realización de
pruebas automáticas (Valdés, 2010)

Características:

 Está disponible en una plataforma de código abierto y se puede utilizar tanto


para pruebas de carga (varios usuarios acceden a los servicios web al mismo
tiempo) como para pruebas de estrés (carga máxima que puede manejar el
servidor web).
 Apache JMeter admite subprocesos múltiples y ofrece una interfaz simple para
organizar y crear secuencias de prueba de rendimiento.
 Admite una variedad de servidores y protocolos: Capaz de realizar pruebas de
rendimiento y carga en una variedad de servidores y navegadores, incluidos
Web: HTTP, HTTPS, SOAP, base de datos a través de JDBC, LDAP, JMS, correo:
POP3, SMTP, etc.
 JMeter también es bastante ampliable y, por lo tanto, compatible con varios
complementos e integraciones de terceros.
 Los administradores de sistemas también pueden usarlo para probar la
funcionalidad del hardware, como el rendimiento del servidor o la velocidad de
la red.
 Puede crear informes de rendimiento personalizados y exportar su información
de rendimiento a archivos CSV.
 Le permite recopilar y analizar estadísticas de rendimiento y un modelo y simular
interacciones web complicadas.
 También ayuda a identificar, abordar y evitar problemas de rendimiento con sus
aplicaciones y servicios web (Geekflare, 2022)

Descargar JMeter (versión Binarie) de la siguiente ruta y descomprimirlo:


https://jmeter.apache.org/download_jmeter.cgi

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 232

Figura 5.1: Descargando JMeter

Nota: Elaboración propia

Para iniciar JMeter, ejecutar el archivo “jmeter.bat” dentro de la carpeta “bin”:

Figura 5.2: Descargando JMeter

Nota: Elaboración propia

Aparecerá la interfaz de JMeter:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 233

Figura 5.3: Interface de JMeter

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 234

Resumen
1. Es una herramienta de testing creada por Apache.

2. Está completamente escrita en JAVA.

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.

4. Mediante un fichero “.jmx” previamente generado que realiza peticiones contra la


aplicación objetivo a testear (dispone de la posibilidad de generar muchos tipos de
peticiones: HTTP, FTP, LDAP, etc.).

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

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 235

5.2. PRUEBAS DE RENDIMIENTO CON JMETER


5.2.1. Crear y ejecutar pruebas de rendimiento con JMeter

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:

Figura 5.4: Levantando el proyecto Tema2Sesion2

Nota: Elaboración propia

Validamos desde el postman que el servicio está funcionando:

Figura 5.5: Validando el postman

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 236

Dentro de Jmeter colocamos un nombre al “Test Plan”: “Tema10” y guardamos el proyecto con
el nombre “Tema10.jmx”

Figura 5.6: Guardando el proyecto

Nota: Elaboración propia

Agregar un “Thread Group”, haciendo clic derecho sobre el nombre del “Test Plan”, luego “Add”,
luego “Threads (Users)” y luego “Thread Group”:

Figura 5.7: Agregar Thread Group

Nota: Elaboración propia

Al “Thread Group” renombrarlo con “Tema2Sesion2” luego donde dice “Number of Threads
(users)” colocar el valor de 5:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 237

Figura 5.8: Configurar Thread Group

Nota: Elaboración propia

Clic derecho sobre el “Thread Group” agregado, ir a Add, luego Sampler y seleccionar “HTTP
Request”:

Figura 5.9: Agregar HTTP Request

Nota: Elaboración propia

Al “HTTP Request” agregado llamarlo “listarPersonas”, luego configurarlo de la siguiente forma,


estos valores obtenerlos del archivo postman:

Figura 5.10: Configurar HTTP Request

Nota: Elaboración propia

Clic derecho sobre el “HTTP Request” y seleccionamos Add, Listener, “View Result Tree”:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 238

Figura 5.11: Agregar “View Result Tree”

Nota: Elaboración propia

Clic derecho sobre el “HTTP Request” y seleccionamos Add, Listener, “Summary Report”:

Figura 5.12: Agregar “Summary Report”

Nota: Elaboración propia

Clic sobre el botón “Start”:

Figura 5.13: Botón “Start”

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 239

Revisamos los resultados obtenidos en “View Result Tree” y “Summary Report”, observamos
que se realizaron 5 peticiones:

Figura 5.14: Resultados de “View Result Tree”

Nota: Elaboración propia

Figura 5.15: Resultados de “Summary Report”

Nota: Elaboración propia

Clic derecho sobre el “HTTP Request” y seleccionamos Add, “Post Processors”, “Bean Shell
PostProcessor”:

Figura 5.16: Agregar “Bean Shell PostProcessor”

Nota: Elaboración propia

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):

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 240

String response=prev.getResponseDataAsString();

FileWriter fstream=new FileWriter("E:/Tema10/listarPersonas-


output.csv",true);

BufferedWriter out=new BufferedWriter(fstream);


out.write(response);
out.write(System.getProperty("line.separator"));
out.close();
fstream.close();

Figura 5.17: Código “Bean Shell PostProcessor”

Nota: Elaboración propia

Limpiamos los resultados tanto de “View Result Tree” y “Summary Report”, haciendo sobre cada
uno clic derecho “Clear”:

Figura 5.18: Clear “View Result Tree”

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 241

Figura 5.19: Clear “View Result Tree”

Nota: Elaboración propia

Clic sobre el botón “Start”:

Figura 5.20: Botón “Start”

Nota: Elaboración propia

Revisamos los resultados obtenidos en “View Result Tree” y “Summary Report”, observamos
que se realizaron 5 peticiones:

Figura 5.21: Resultados de “View Result Tree”

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 242

Figura 5.22: Resultados de “Summary Report”

Nota: Elaboración propia

Observamos que se ha creado el archivo “listarPersonas-output.csv” en la ruta indicada con el


resultado de las 5 ejecuciones:

Figura 5.23: Resultados de “listarPersonas-output.csv”

Nota: Elaboración propia

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:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 243

Figura 5.24: Configurar HTTP Request

Nota: Elaboración propia

Clic derecho sobre el “HTTP Request” y seleccionamos Add, Listener, “View Result Tree” y
“Summary Report”, luego agregamos el componente “CSV Data Set Config”:

Figura 5.25: Agregar “CSV Data Set Config”

Nota: Elaboración propia

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:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 244

Figura 5.26: Archivo “obtenerPersona-input.csv”

Nota: Elaboración propia

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:

Figura 5.27: Configurar “CSV Data Set Config”

Nota: Elaboración propia

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();

FileWriter fstream=new FileWriter("E:/Tema10/obtenerPersona-


output.csv",true);

BufferedWriter out=new BufferedWriter(fstream);


out.write(response);
out.write(System.getProperty("line.separator"));

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 245

out.close();
fstream.close();
Figura 5.28: Código “Bean Shell PostProcessor”

Nota: Elaboración propia

Clic sobre el botón “Start” y revisamos los resultados obtenidos en “View Result Tree” y
“Summary Report”, observamos que se realizaron 5 peticiones:

Figura 5.29: Resultados de “View Result Tree”

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 246

Figura 5.30: Resultados de “Summary Report”

Nota: Elaboración propia

Observamos que se ha creado el archivo “obtenerPersona-output.csv” en la ruta indicada con el


resultado de las 5 ejecuciones:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 247

Figura 5.31: Resultados de “obtenerPersona-output.csv”

Nota: Elaboración propia

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}"
}
}

Figura 5.32: Configurar HTTP Request

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 248

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

Figura 5.33: Archivo “registrarPersona-input.csv”

Nota: Elaboración propia

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:

Figura 5.34: Configurar “CSV Data Set Config”

Nota: Elaboración propia

Clic derecho sobre el “HTTP Request” y seleccionamos Add, “Post Processors”, “Bean Shell
PostProcessor”, dentro del componente “Bean Shell PostProcessor”, agregamos el siguiente

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 249

código (asegurarse de que la ruta “E:/Tema10” exista en su PC o modificarla por una ruta
existente):

String response=prev.getResponseDataAsString();

FileWriter fstream=new FileWriter("E:/Tema10/registrarPersona-


output.csv",true);

BufferedWriter out=new BufferedWriter(fstream);


out.write(response);
out.write(System.getProperty("line.separator"));
out.close();
fstream.close();

Figura 5.35: Código “Bean Shell PostProcessor”

Nota: Elaboración propia

Clic sobre el botón “Start”, observamos que aparece el siguiente error:

Figura 5.36: Error ejecución

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 250

Clic derecho sobre el “HTTP Request” y seleccionamos Add, “Config Element”, “HTTP Header
Manager”:
Figura 5.37: Componente “HTTP Header Manager”

Nota: Elaboración propia

Clic sobre el botón “Add” y agregamos la variable “Content-Type” con el valor


“application/json”:

Figura 5.38: Variable “Content-Type”

Nota: Elaboración propia

Clic sobre el botón “Start” y revisamos los resultados obtenidos en “View Result Tree” y
“Summary Report”, observamos que se realizaron 5 peticiones:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 251

Figura 5.39: Resultados de “View Result Tree”

Nota: Elaboración propia

Figura 5.40: Resultados de “Summary Report”

Nota: Elaboración propia

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}"
}
}

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 252

Figura 5.41: Configurar HTTP Request

Nota: Elaboración propia

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

Figura 5.42: Archivo “modificarPersona-input.csv”

Nota: Elaboración propia

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:

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 253

Figura 5.43: Configurar “CSV Data Set Config”

Nota: Elaboración propia

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();

FileWriter fstream=new FileWriter("E:/Tema10/modificarPersona-


output.csv",true);

BufferedWriter out=new BufferedWriter(fstream);


out.write(response);
out.write(System.getProperty("line.separator"));
out.close();
fstream.close();

Figura 5.44: Código “Bean Shell PostProcessor”

Nota: Elaboración propia

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 254

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”:

Figura 5.45: Variable “Content-Type”

Nota: Elaboración propia

Clic sobre el botón “Start” y revisamos los resultados obtenidos en “View Result Tree” y
“Summary Report”, observamos que se realizaron 5 peticiones:

Figura 5.46: Resultados de “View Result Tree”

Nota: Elaboración propia

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 255

Figura 5.47: Resultados de “Summary Report”

Nota: Elaboración propia

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

Nota: Elaboración propia

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):

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 256

Figura 5.49: Archivo “eliminarPersona-input.csv”

Nota: Elaboración propia

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:

Figura 5.50: Configurar “CSV Data Set Config”

Nota: Elaboración propia

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();

FileWriter fstream=new FileWriter("E:/Tema10/eliminarPersona-


output.csv",true);

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 257

BufferedWriter out=new BufferedWriter(fstream);


out.write(response);
out.write(System.getProperty("line.separator"));
out.close();
fstream.close();

Figura 5.51: Código “Bean Shell PostProcessor”

Nota: Elaboración propia

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”:

Figura 5.52: Variable “Content-Type”

Nota: Elaboración propia

Clic sobre el botón “Start” y revisamos los resultados obtenidos en “View Result Tree” y
“Summary Report”, observamos que se realizaron 5 peticiones:

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 258

Figura 5.53: Resultados de “View Result Tree”

Nota: Elaboración propia

Figura 5.54: Resultados de “Summary Report”

Nota: Elaboración propia

5.2.2. Análisis de resultados

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”

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 259

Resumen
1. JMeter fue diseñado para realizar pruebas de carga en servidores o aplicativos Web por
medio del protocolo HTTP.

2. JMeter proporciona la capacidad de programación BeanShell, puede escribir un script de


prueba más flexible.

3. JMeter proporciona capacidades de extensión más avanzadas, lo que le permite definir y


ampliar el nuevo soporte de protocolo.

4. JMeter es relativamente ligero y de código abierto, con alta aceptación de la comunidad y


fácil entrada.

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

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN


PRUEBAS DE SOFTWARE 260

Bibliografía
• Adzic, G. (2009). Mockito in six easy examples. https://gojko.net/2009/10/23/mockito-in-
six-easy-examples/

• Calikli,G.; Uzundağ, b. y Bener, A. (2010). Confirmation Bias in Software Development and


Testing: An Analysis of the Effects of Company Size, Experience and Reasoning Skills.
https://www.researchgate.net/publication/235430372_Confirmation_Bias_in_Software_
Development_and_Testing_An_Analysis_of_the_Effects_of_Company_Size_Experience_a
nd_Reasoning_Skills

• Club de Tecnología (2023). Que es Appium: Automatización de aplicaciones nativas, web e


híbridas. https://www.clubdetecnologia.net/blog/2017/que-es-appium-automatizacion-
de-aplicaciones-nativas-web-e-hibridas/

• 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.

• Hans, M. (2015). Appium Essentials (1.ª ed.). Editorial Packt Publishing.

• Hernández, R. (2018). Automatizando el testing de web móviles: Appium + Nightwatch.js.

• Hiberus TECNOLOGÍA. (2022). BDD Testing. ¿Cómo funciona el Behavior Driven


Development?. https://www.hiberus.com/crecemos-contigo/bdd-behavior-driven-
developement/

• 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/

• JhonyCode Web Dev (s.f.). Selenium una alternativa a pruebas funcionales.


https://jhonyarticle.wordpress.com/2017/10/02/selenium-una-alternativa-a-pruebas-
funcionales/

ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN IES CIBERTEC


PRUEBAS DE SOFTWARE 261

• Matam, S. (2017). Pro Apache JMeter: Web Application Performance Testing (1.ª ed.).
Editorial Apress.

• Medina, E. (2022). Conoce qué es Android Debug Bridge (ADB).


https://www.muycomputer.com/2022/01/14/android-debug-bridge-adb/

• Rodríguez, T. (2013). Rest-assured, framework para testear y validar nuestros servicios


REST. Genbeta. https://www.genbeta.com/desarrollo/rest-assured-framework-para-
testear-y-validar-nuestros-servicios-rest

• Satish Sheti. (s.f.). Apache Jmeter: Todo lo que necesitas saber.


https://geekflare.com/es/apache-jmeter-guide/

• Soto, M. (2019). Automatización de Pruebas Funcionales: Serenity BDD+Screen Play+Java.


https://medium.com/@marcela.soto/automatizaci%C3%B3n-de-pruebas-funcionales-
serenity-bdd-screen-play-java-942be8217fca

• 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

• Testeando Software (2015). Selenium. Introducción a la automatización de pruebas de


navegación web. https://testeandosoftware.com/selenium-introduccion-la-
automatizacion-de-pruebas-de-navegacion-web/

• Una QA En apuros. (2019). 048 – Appium


Inspector.https://unaqaenapuros.wordpress.com/2019/02/27/048-appium-inspector/

• Valdés, M. (2010). “Propuesta de un procedimiento para la evaluación del rendimiento de


las bases de datos basadas en PostgreSQL para software de gestión”. Universidad de las
Ciencias Informáticas.
https://repositorio.uci.cu/jspui/bitstream/ident/TD_02937_10/1/TD_02937_10.pdf

• Vergara, S. (2019). ¿Qué es BDD (Behavior Driven Development)?. OTDO.


https://www.itdo.com/blog/que-es-bdd-behavior-driven-development/

• 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.

• Wordpress (s.f.). Prueba de desempeño. https://poo2018testing.wordpress.com/prueba-


de-desempeno/

IES CIBERTEC ESCUELA DE TECNOLOGÍAS DE LA INFORMACIÓN

También podría gustarte