SlideShare ist ein Scribd-Unternehmen logo
1 von 90
Introducción y novedades de
JUnit 5
Universidad Rey Juan Carlos
04/07/2018
Boni García
boni.garcia@urjc.es http://bonigarcia.github.io/
@boni_gg https://github.com/bonigarcia
Boni García
• Soy doctor en sistemas telemáticos por la Universidad
Politécnica de Madrid (UPM) desde 2011
• Tesis doctoral centrada en las pruebas en el software (testing)
• Actualmente trabajo como:
• Investigador en la Universidad Rey Juan Carlos (URJC)
• Profesor en el Centro Universitario de Tecnología y Arte Digital
(U-tad)
• Participo activamente en múltiples proyectos open source:
• Comunidades: ElasTest, Kurento
• Proyectos propios: WebDriverManager, Selenium-Jupiter, DualSub
• Soy autor del libro Mastering Software Testing with JUnit 5
Pack Publishing (octubre 2017)
Contenidos
1. Introducción a las pruebas en el software
2. Motivación y arquitectura de JUnit 5
3. Jupiter: el nuevo modelo de programación de JUnit 5
4. Modelo de extensiones en JUnit 5
5. Selenium-Jupiter
6. Conclusiones
1. Introducción a las pruebas en el software
• La calidad en el software es un término bastante ambiguo en el
mundo de la ingeniería de software. Según Roger Pressman:
Software quality is an effective software process applied in a
manner that creates a useful product that provides measurable
value for those who produce it and those who use it
• El conjunto de técnicas destinadas a asegurar la calidad (QA, Quality
Assurance) en un proyecto software se conocen como Verificación y
Validación (V&V, Verification & Validation). Según Barry Boehm:
Are we building the product right? (verification)
Are we building the right product? (validation)
1. Introducción a las pruebas en el software
• Hay dos tipos de técnicas dentro de V&V:
1. Análisis (estático): evaluación del software (ya sea código, modelos,
documentación, etc.) sin ejecutar el propio software
• Revisión de pares: Collaborator, Crucible, Gerrit …
• Análisis automático (linters): SonarQube, Checkstyle, FindBugs, PMD …
2. Pruebas (dinámico): evaluación del software observando un
resultado esperado de la ejecución de una parte o todo el sistema
(caso de prueba) y dar un veredicto sobre la misma
1. Introducción a las pruebas en el software
Test
Level
Unit
Integration
System
Acceptance
Method
Black-box
White-box
Non-
functional
Type
Manual
Automated
• Pruebas unitarias (componentes aislados)
• Pruebas de integración (diferentes componentes)
• Pruebas de sistema (todos los componentes)
• Pruebas de aceptación (pruebas de usuario)
• Pruebas de caja negra (funcionales)
• Pruebas de caja blanca (estructurales)
• Pruebas no funcionales (rendimiento, seguridad,…)
• Pruebas manuales
• Pruebas automáticas (frameworks de prueba)
1. Introducción a las pruebas en el software
• Las pruebas automáticas según Elfriede Dustin:
Application and implementation of software technology
throughout the entire software testing life cycle with the goal to
improve efficiencies and effectiveness
• Las pruebas automáticas son más efectivas cuando se implementan
en base a un framework. Según Martin Fowler:
A library is essentially a set of functions that you can call,
these days usually organized into classes. Each call does
some work and returns control to the client. A framework
embodies some abstract design, with more behavior built
in. In order to use it you need to insert your behavior into
various places in the framework either by subclassing or by
plugging in your own classes. The framework's code then
calls your code at these points.
Library
Your code
Framework
contains
callscalls
1. Introducción a las pruebas en el software
Acceptance
System
Integration
Unit
Development
testing
(verification)
User testing
(validation)
• Pirámide de tests:
Contenidos
1. Introducción a las pruebas en el software
2. Motivación y arquitectura de JUnit 5
3. Jupiter: el nuevo modelo de programación de JUnit 5
4. Modelo de extensiones en JUnit 5
5. Selenium-Jupiter
6. Conclusiones
2. Motivación y arquitectura de JUnit 5
• JUnit es el framework más
utilizado en la comunidad Java y
uno de los más influyentes en la
ingeniería de software en
general (precursor de la familia
xUnit)
• La última versión de JUnit 4 fue
liberada en diciembre de 2014
• JUnit 4 tiene importantes
limitaciones que han propiciado
el rediseño completo del
framework en JUnit 5
The Top 100 Java libraries on GitHub (by OverOps)
2. Motivación y arquitectura de JUnit 5
• Los principales inconvenientes de JUnit 4 son:
1. JUnit 4 es monolítico. Toda las funciones de JUnit 4 son
proporcionadas por un único componente, de forma que
mecanismos como el descubrimiento de tests y la ejecución de los
mismos están muy acoplados
junit.jar
Tests
IDEs Build tools
3rd party
frameworks
Extensions
2. Motivación y arquitectura de JUnit 5
• Los principales inconvenientes de JUnit 4 son:
2. Los casos de prueba se ejecutan en JUnit 4 mediante unas clases
especiales llamadas Test Runners. Estos runners tienen una
limitación fundamental: no se pueden componer
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class MyTest1 {
@Test
public void myTest() {
// my test code
}
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
public class MyTest2 {
@Test
public void myTest() {
// my test code
}
}
2. Motivación y arquitectura de JUnit 5
• Los principales inconvenientes de JUnit 4 son:
3. Para mejorar la gestión del ciclo de vida de tests en JUnit 4 se
desarrollaron las reglas (Test Rules), implementadas con las
anotaciones @Rule y @ClassRule. El inconveniente es que puede
llegar a ser complicado gestionar ambos ámbitos de forma
simultánea (runners y rules)
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
public class MyTest3 {
@Rule
public ErrorCollector errorCollector = new ErrorCollector();
@Test
public void myTest() {
// my test code
}
}
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class MyTest4 {
@ClassRule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Test
public void myTest() {
// my test code
}
}
2. Motivación y arquitectura de JUnit 5
• Para intentar solucionar estos problemas, en julio de 2015 Johannes
Link y Mark Philipp pusieron en marcha una campaña para recaudar
fondos y crear una nueva versión de JUnit
• Esta campaña se conoció como JUnit Lambda crowdfunding campaign
• Gracias a esta campaña se puso en marcha el equipo de JUnit 5, con
miembros de diferentes compañías (Eclipse, Gradle, e IntelliJ entre
otras)
2. Motivación y arquitectura de JUnit 5
• Arquitectura de JUnit 5:
La plataforma JUnit
(Platform) es un
componente que actúa
de ejecutor genérico para
pruebas que se ejecutan
en la JVM
Test de versiones
anteriores de JUnit (3 y 4)
serán ejecutados a través
del componente Vintage
Jupiter es componente
que implementa el nuevo
modelo de programación
y extensión en JUnit 5
La idea es que otros
frameworks (e.g.
Spock, Cucumber)
pueden ejecutar sus
propios casos de
prueba realizando una
extensión de la
plataforma
Los cliente
programáticos usan la
plataforma para el
descubrimiento y la
ejecución de los tests
2. Motivación y arquitectura de JUnit 5
• Hay tres tipos de módulos:
1. Test API: Módulos usados
por testers para implementar
casos de prueba
2. Test Engine SPI: Módulos
extendidos para un
framework de pruebas Java
para la ejecución de un
modelo concreto de tests
3. Test Launcher API: Módulos
usados por clientes
programáticos para el
descubrimiento y ejecución
de tests
Contenidos
1. Introducción a las pruebas en el software
2. Motivación y arquitectura de JUnit 5
3. Jupiter: el nuevo modelo de programación de JUnit 5
4. Modelo de extensiones en JUnit 5
5. Selenium-Jupiter
6. Conclusiones
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Los tests básicos en Jupiter son muy
parecidos a JUnit 4:
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class BasicJUnit4Test {
@BeforeClass
public static void setupAll() {
// setup all tests
}
@Before
public void setup() {
// setup each test
}
@Test
public void test() {
// exercise and verify SUT
}
@After
public void teardown() {
// teardown each test
}
@AfterClass
public static void teardownAll() {
// teardown all tests
}
}
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Los tests básicos en Jupiter son muy
parecidos a JUnit 4:
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class BasicJUnit5Test {
@BeforeAll
static void setupAll() {
// setup all tests
}
@BeforeEach
void setup() {
// setup each test
}
@Test
void test() {
// exercise and verify SUT
}
@AfterEach
void teardown() {
// teardown each test
}
@AfterAll
static void teardownAll() {
// teardown all tests
}
}
El nombre de las
anotaciones que
gestionan el ciclo
de vida básico de
los tests ha
cambiado en
Jupiter
Se elimina la
necesidad que las
clases y los
métodos de
prueba tengan
que ser public
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Podemos ver el ciclo de vida de un caso de prueba en JUnit 5 de la
siguiente manera:
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Actualmente se pueden ejecutar test de JUnit 5 de diferentes formas:
1. Mediante herramienta de gestión y construcción de proyectos Java
(build tools)
• Maven
• Gradle
• Ant
2. Mediante un Entorno de Desarrollo Integrado (IDE)
• IntelliJ IDEA 2016.2+
• Eclipse 4.7+
• Visual Studio (Java Extension Pack, enero 2018)
3. Mediante una herramienta propia de JUnit 5
• Console Launcher (standalone jar que permite ejecutar tests JUnit 5)
java -jar junit-platform-console-standalone-version.jar <Options>
3. Jupiter: el nuevo modelo de programación de JUnit 5
• En Maven, la gestión de proyectos Java se realiza a través de ciclos de
vida independientes:
• Clean: Para limpiar el proyecto
• Default: Para la gestión propiamente dicha
• Site: Para la documentación
• Cada ciclo de vida está compuesto por fases que se ejecutan en
cascada (compilar, empaquetar, ejecutar las pruebas, desplegar, etc.):
Clean Lifecycle
pre-clean
clean
post-clean
Default Lifecycle
validate generate-test-sources package
initialize process-test-sources pre-integration-test
generate-sources generate-test-resources integration-test
process-sources process-test-resources post-integration-test
generate-resources test-compile verify
process-resources process-test-classes install
compile test deploy
process-classes prepare-package
Site Lifecycle
pre-site
site
post-site
site-deploy
Se usa la línea de comandos
para invocar la ejecución de
un ciclo de vida hasta una
determinada fase. Ejemplo:
mvn package
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Cada fase está formada por objetivos (goals)
• Los objetivos pueden ser específicos del tipo de empaquetado del
proyecto (packaging)
• Los objetivos son ejecutados por diferentes plugins
Clean Lifecycle
Fase Objetivo
clean clean:clean
Default Lifecycle (Packaging jar)
Fase Objetivo
process-resources resources:resources
compile compiler:compile
process-test-resources resources:testResources
test-compile compiler:testCompile
test surefire:test
package jar:jar
install install:install
deploy deploy:deploy
Clean Lifecycle
Fase Objetivo
site site:site
site-deploy site:deploy
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Los plugins y las versiones por defecto en los diferentes ciclos de vida
se definen en un fichero llamado default-bindings.xml que está
incluido en la distribución binaria de Maven
• Por ejemplo, para el empaquetado jar del ciclo de vida por defecto:
Default Lifecycle (Packaging jar)
Fase Objetivo Plugin (artifactId) Version
process-resources resources:resources maven-resources-plugin 2.6
compile compiler:compile maven-compiler-plugin 3.1
process-test-resources resources:testResources maven-resources-plugin 2.6
test-compile compiler:testCompile maven-compiler-plugin 3.1
test surefire:test maven-surefire-plugin 2.12.4
package jar:jar maven-jar-plugin 2.4
install install:install maven-install-plugin 2.4
deploy deploy:deploy maven-deploy-plugin 2.7
El groupId de todos
estos plugins es
org.apache.maven.
plugins
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Para las pruebas, nos interesan dos plugins de Maven:
1. maven-surefire-plugin: Plugin Maven por defecto para
ejecutar las pruebas antes del empaquetado (teóricamente
unitarias) > mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< io.github.bonigarcia:junit5-failsafe >----------------
[INFO] Building junit5-failsafe 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ junit5-failsafe ---
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ junit5-failsafe ---
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ junit5-failsafe ---
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ junit5-failsafe ---
[INFO] --- maven-surefire-plugin:2.22.0:test (default-test) @ junit5-failsafe ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running io.github.bonigarcia.ExecutedBySurefirePluginTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.016 s - in io.github.bonigarcia.ExecutedBySurefirePluginTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.423 s
[INFO] Finished at: 2018-07-02T16:44:20+02:00
[INFO] ------------------------------------------------------------------------
3. Jupiter: el nuevo modelo de programación de JUnit 5
2. maven-failsafe-plugin: Plugin Maven para ejecutar las
pruebas posteriores al empaquetado (teóricamente de integración)
> mvn verify
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< io.github.bonigarcia:junit5-failsafe >----------------
[INFO] Building junit5-failsafe 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ junit5-failsafe ---
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ junit5-failsafe ---
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ junit5-failsafe ---
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ junit5-failsafe ---
[INFO] --- maven-surefire-plugin:2.22.0:test (default-test) @ junit5-failsafe ---
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ junit5-failsafe ---
[INFO] --- maven-failsafe-plugin:2.22.0:integration-test (default) @ junit5-failsafe ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running io.github.bonigarcia.ExecutedByFailsafePluginIT
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.009 s - in io.github.bonigarcia.ExecutedByFailsafePluginIT
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-failsafe-plugin:2.22.0:verify (default) @ junit5-failsafe ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.887 s
[INFO] Finished at: 2018-07-02T16:42:39+02:00
[INFO] ------------------------------------------------------------------------
3. Jupiter: el nuevo modelo de programación de JUnit 5
• La siguiente tabla resume las principales diferencias entre Surefire y
Failsafe :
Surefire Failsafe
Es un plugin por defecto del ciclo de vida No es un plugin por defecto, con lo que para poder
usarlo es necesario decláralo en el pom.xml (junto a
los objetivos que cubre)
Los tests se ejecutan y se validan en la fase test:
mvn test
Los tests se ejecutan en la fase integration-test y
se validan en la fase verify:
mvn verify
Conceptualmente se usa para tests unitarios Conceptualmente se usa para tests de integración
Por defecto los tests se localizan siguiendo un patrón
en los nombres:
• **/Test*.java
• **/*Test.java
• **/*Tests.java
• **/*TestCase.java
Por defecto los tests se localizan siguiendo un patrón
en los nombres:
• **/IT*.java
• **/*IT.java
• **/*ITCase.java
La versión por defecto de Surefire está definida en un
fichero interno de Maven
Por defecto se usa la última versión disponible de
Failsafe
Como vamos a ver,
en JUnit 5 podemos
usar ambos plugins
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Ejecutar JUnit 5 desde Maven (versiones 5.0.x and 5.1.x):
<properties>
<junit.jupiter.version>5.0.3</junit.jupiter.version>
<junit.platform.version>1.0.3</junit.platform.version>
<maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>${junit.platform.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
Todos los ejemplos están
disponibles en GitHub
https://github.com/bonigarcia/
mastering-junit5
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
Para ser precisos, se necesita la API en
tiempo de compilación y el engine en
tiempo de ejecución
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Ejecutar JUnit 5 desde Maven (versiones 5.2+):
<properties>
<junit.jupiter.version>5.2.0</junit.jupiter.version>
<maven-surefire-plugin.version>2.22.0</maven-surefire-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
</plugins>
</build>
La versión 2.22.0 de
Surefire (y Failsafe)
tienen soporte nativo de
JUnit 5
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Ejecutar JUnit 5 desde Maven (versiones 5.2+):
<properties>
<junit.jupiter.version>5.2.0</junit.jupiter.version>
<maven-surefire-plugin.version>2.22.0</maven-surefire-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Si además de Surefire
queremos usar Failsafe,
hay que declararlo
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Ejecutar JUnit 5 desde Gradle (versiones inferiores de Gradle 4.8):
buildscript {
ext {
junitPlatformVersion = '1.0.0'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.junit.platform:junit-platform-gradle-plugin:${junitPlatformVersion}")
}
}
repositories {
mavenCentral()
}
ext {
junitJupiterVersion = '5.0.0'
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.junit.platform.gradle.plugin'
compileTestJava {
sourceCompatibility = 1.8
targetCompatibility = 1.8
options.compilerArgs += '-parameters'
}
dependencies {
testCompile("org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}")
testRuntime("org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}")
}
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Ejecutar JUnit 5 desde Gradle (a partir de Gradle 4.8):
repositories {
mavenCentral()
}
ext {
junitJupiterVersion = '5.2.0'
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
compileTestJava {
sourceCompatibility = 1.8
targetCompatibility = 1.8
options.compilerArgs += '-parameters'
}
dependencies {
testCompile("org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}")
}
Gradle 4.8 tiene
soporte nativo
de JUnit 5
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Ejecutar JUnit 5 desde IDEs como IntelliJ y Eclipse:
3. Jupiter: el nuevo modelo de programación de JUnit 5
• De forma conceptual, una aserción (o predicado) está formada por
• Datos esperados, obtenidos de lo que se conoce como oráculo (típicamente la
especificación del SUT)
• Datos reales, obtenidos de ejercitar el sistema bajo pruebas (SUT)
• Un operador lógico que compara ambos valores
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Podemos ver el ciclo de vida un caso de prueba en JUnit 5 (con
aserciones) de la siguiente manera:
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Las aserciones básicas en Jupiter son las siguientes:
Aserción Descripción
fail Hace fallar un test proporcionando un mensaje de error u excepción
assertTrue Evalúa si una condición es cierta
assertFalse Evalúa si una condición es false
assertNull Evalúa si un objeto es null
assertNotNull Evalúa si un objeto no es null
assertEquals Evalúa si un objeto es igual a otro
assertNotEquals Evalúa si un objeto no es igual a otro
assertArrayEquals Evalúa si un array es igual a otros
assertIterableEquals Evalúa si dos objetos iterables son iguales
assertLinesMatch Evlúa si dos listas de String son iguales
assertSame Evalúa si un objeto es el mismo que otro
assertNotSame Evalúa si un objeto no es el mismo que otro
Métodos estáticos
de la clase
Assertions
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Un grupo de aserciones se evalúa mediante assertAll:
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class GroupedAssertionsTest {
@Test
void groupedAssertions() {
Address address = new Address("John", "Smith");
assertAll("address", () -> assertEquals("John", address.getFirstName()),
() -> assertEquals("User", address.getLastName()));
}
}
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running io.github.bonigarcia.GroupedAssertionsTest
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time
elapsed: 0.124 sec <<< FAILURE! - in
io.github.bonigarcia.GroupedAssertionsTest
groupedAssertions() Time elapsed: 0.08 sec <<< FAILURE!
org.opentest4j.MultipleFailuresError:
address (1 failure)
expected: <User> but was: <Smith>
at
io.github.bonigarcia.GroupedAssertionsTest.groupedAssertio
ns(GroupedAssertionsTest.java:32)
Results :
Failed tests:
GroupedAssertionsTest.groupedAssertions:32 address (1
failure)
expected: <User> but was: <Smith>
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
En este ejemplo la
segunda aserción no se
cumple
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Las ocurrencia de excepciones se implementa mediante la
aserción assertThrows:
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
class ExceptionTest {
@Test
void exceptionTesting() {
Throwable exception = assertThrows(IllegalArgumentException.class,
() -> {
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}
}
En este ejemplo el test pasará ya que
estamos esperando la excepción
IllegalArgumentException, y de
decho ocurre dentro una expression
lambda
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Para evaluar timeouts podemos usar la aserción assertTimeout:
import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import org.junit.jupiter.api.Test;
class TimeoutExceededTest {
@Test
void timeoutNotExceeded() {
assertTimeout(ofMinutes(2), () -> {
// Perform task that takes less than 2 minutes
});
}
@Test
void timeoutExceeded() {
assertTimeout(ofMillis(10), () -> {
// Simulate task that takes more than 10 ms
Thread.sleep(100);
});
}
}
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running io.github.bonigarcia.TimeoutExceededTest
Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time
elapsed: 0.18 sec <<< FAILURE! - in
io.github.bonigarcia.TimeoutExceededTest
timeoutExceeded() Time elapsed: 0.126 sec <<< FAILURE!
org.opentest4j.AssertionFailedError: execution exceeded
timeout of 10 ms by 90 ms
at
io.github.bonigarcia.TimeoutExceededTest.timeoutExceeded(
TimeoutExceededTest.java:36)
Results :
Failed tests:
TimeoutExceededTest.timeoutExceeded:36 execution
exceeded timeout of 10 ms by 90 ms
Tests run: 2, Failures: 1, Errors: 0, Skipped: 0
Este test
pasará
Este test
fallará
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Si queremos aserciones todavía más avanzadas, se recomienda
usar librerías específicas, como por ejemplo Hamcrest:
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.jupiter.api.Test;
class HamcrestTest {
@Test
void assertWithHamcrestMatcher() {
assertThat(2 + 1, equalTo(3));
assertThat("Foo", notNullValue());
assertThat("Hello world", containsString("world"));
}
}
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running io.github.bonigarcia.HamcrestTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time
elapsed: 0.059 sec - in io.github.bonigarcia.HamcrestTest
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
http://hamcrest.org/
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Podemos declarar nombres personalizados a los métodos de
prueba mediante la anotación @DisplayName:
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("A special test case")
class DisplayNameTest {
@Test
@DisplayName("Custom test name containing spaces")
void testWithDisplayNameContainingSpaces() {
}
@Test
@DisplayName("╯°□°)╯")
void testWithDisplayNameContainingSpecialCharacters() {
}
@Test
@DisplayName("😱")
void testWithDisplayNameContainingEmoji() {
}
}
IntelliJ IDEA: 2016.2+
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Las clases y métodos de test en JUnit 5 se pueden etiquetar
usando la anotación @Tag
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@Tag("functional")
class FunctionalTest {
@Test
void test1() {
System.out.println("Functional Test 1");
}
@Test
void test2() {
System.out.println("Functional Test 2");
}
}
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@Tag("non-functional")
class NonFunctionalTest {
@Test
@Tag("performance")
@Tag("load")
void test1() {
System.out.println("Non-Functional Test 1 (Performance/Load)");
}
@Test
@Tag("performance")
@Tag("stress")
void test2() {
System.out.println("Non-Functional Test 2 (Performance/Stress)");
}
@Test
@Tag("security")
void test3() {
System.out.println("Non-Functional Test 3 (Security)");
}
@Test
@Tag("usability")
void test4() {
System.out.println("Non-Functional Test 4 (Usability)");
}
}
• Estas etiquetas se pueden usar
después para el descubrimiento y
ejecución de los test (filtrado)
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Para filtrar por tags en
Maven:
<properties>
<junit-jupiter.version>5.3.0-M1</junit-jupiter.version>
<junit-platform.version>1.3.0-M1</junit-platform.version>
<maven-surefire-plugin.version>2.22.0</maven-surefire-plugin.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>${junit-platform.version}</version>
</dependency>
</dependencies>
<configuration>
<properties>
<includeTags>functional</includeTags>
<excludeTags>non-functional</excludeTags>
</properties>
</configuration>
</plugin>
</plugins>
</build>
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running io.github.bonigarcia.FunctionalTest
Functional Test 2
Functional Test 1
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time
elapsed: 0.075 sec - in
io.github.bonigarcia.FunctionalTest
Running io.github.bonigarcia.NonFunctionalTest
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time
elapsed: 0 sec - in
io.github.bonigarcia.NonFunctionalTest
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Para filtrar por tags en
Gradle:
test {
useJUnitPlatform {
includeTags 'non-functional'
excludeTags 'functional'
includeEngines 'junit-jupiter'
excludeEngines 'junit-vintage'
}
filter {
includeTestsMatching '.*Spec'
includeTestsMatching '.*Test'
includeTestsMatching '.*Tests'
includeTestsMatching 'io.github.bonigarcia.*'
}
testLogging {
events "passed", "skipped", "failed"
}
}
mastering-junit5junit5-tagging-filtering>gradle test --rerun-tasks
> Task :junit5-tagging-filtering:test
io.github.bonigarcia.NonFunctionalTest > testOne() PASSED
io.github.bonigarcia.NonFunctionalTest > testTwo() PASSED
io.github.bonigarcia.NonFunctionalTest > testThree() PASSED
io.github.bonigarcia.NonFunctionalTest > testFour() PASSED
BUILD SUCCESSFUL in 2s
2 actionable tasks: 2 executed
3. Jupiter: el nuevo modelo de programación de JUnit 5
• La anotación @Disabled se usa para deshabilitar tests. Se
puede usar tanto a nivel de clase como a nivel de método
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
class DisabledTest {
@Disabled
@Test
void skippedTest() {
}
}
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running io.github.bonigarcia.DisabledTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 1, Time
elapsed: 0.03 sec - in io.github.bonigarcia.DisabledTest
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 1
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@Disabled("All test in this class will be skipped")
class AllDisabledTest {
@Test
void skippedTest1() {
}
@Test
void skippedTest2() {
}
}
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running io.github.bonigarcia.AllDisabledTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 1, Time elapsed:
0.059 sec - in io.github.bonigarcia.AllDisabledTest
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 1
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Desde JUnit 5.1 existe la anotación @DisabledOnOs para
deshabilitar tests en función del sistema operativo
import static org.junit.jupiter.api.condition.OS.LINUX;
import static org.junit.jupiter.api.condition.OS.MAC;
import static org.junit.jupiter.api.condition.OS.WINDOWS;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
class BuiltinDisabledOnOsTest {
@DisabledOnOs(LINUX)
@Test
void notLinuxTest() {
System.out.println("Disabled on Linux");
}
@DisabledOnOs(WINDOWS)
@Test
void notWinTest() {
System.out.println("Disabled on Windows");
}
@DisabledOnOs(MAC)
@Test
void notMacTest() {
System.out.println("Disabled on Mac");
}
}
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Las asunciones sirven para
ignorar un test (o una parte del
mismo) en base a una
condición
• Hay tres asunciones en JUnit 5:
assumeTrue, assumeFalse,
y assumingThat
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;
import org.junit.jupiter.api.Test;
class AssumptionsTest {
@Test
void assumeTrueTest() {
assumeTrue(false);
fail("Test 1 failed");
}
@Test
void assumeFalseTest() {
assumeFalse(this::getTrue);
fail("Test 2 failed");
}
private boolean getTrue() {
return true;
}
@Test
void assummingThatTest() {
assumingThat(false, () -> fail("Test 3 failed"));
}
}
-------------------------------------------------------
T E S T S
-------------------------------------------------------
org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry
loadTestEngines
INFORMACIÓN: Discovered TestEngines with IDs: [junit-jupiter]
Running io.github.bonigarcia.AssumptionsTest
Tests run: 3, Failures: 0, Errors: 0, Skipped: 2, Time elapsed:
0.093 sec - in io.github.bonigarcia.AssumptionsTest
Results :
Tests run: 3, Failures: 0, Errors: 0, Skipped: 2
La asunción
assumingThat se
usa para condicionar
la ejecución de una
parte del test
3. Jupiter: el nuevo modelo de programación de JUnit 5
• La anotación @RepeatedTest permite repetir la ejecución de un
test un número determinado de veces
• Cada repetición se comportará exactamente igual que un test
normal, con su correspondiente ciclo de vida
import org.junit.jupiter.api.RepeatedTest;
class SimpleRepeatedTest {
@RepeatedTest(5)
void test() {
System.out.println("Repeated test");
}
}
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running io.github.bonigarcia.SimpleRepeatedTest
Repeated test
Repeated test
Repeated test
Repeated test
Repeated test
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed:
0.11 sec - in io.github.bonigarcia.SimpleRepeatedTest
Results :
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Los test parametrizados reutilizan la misma lógica con diferentes
datos de prueba
• Para implementar este tipo de test, lo primero es añadir el módulo
junit-jupiter-params en nuestro proyecto
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
dependencies {
testCompile("org.junit.jupiter:junit-jupiter-params:${junitJupiterVersion}")
}
3. Jupiter: el nuevo modelo de programación de JUnit 5
• Los pasos para implementar un test parametrizado son:
1. Usar la anotación @ParameterizedTest para declarar un test
como parametrizado
2. Elegir un proveedor de argumentos (argument provider)
Arguments provider Descripción
@ValueSource Usado para especificar un array de valores String, int, long, o
double
@EnumSource Usado para especificar valores enumerados (java.lang.Enum)
@MethodSource Usado para especificar un método estático de la clase que proporciona
un Stream de valores
@CsvSource Usado para especificar valores separados por coma, esto es, en
formato CSV (comma-separated values)
@CsvFileSource Usado para especificar valores en formato CSV en un fichero localizado
en el classpath
@ArgumentsSource Usado para especificar una clase que implementa el interfaz
org.junit.jupiter.params.provider.ArgumentsProvider
3. Jupiter: el nuevo modelo de programación de JUnit 5
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class ValueSourcePrimitiveTypesParameterizedTest {
@ParameterizedTest
@ValueSource(ints = { 0, 1 })
void testWithInts(int argument) {
System.out
.println("Parameterized test with (int) argument: " + argument);
assertNotNull(argument);
}
@ParameterizedTest
@ValueSource(longs = { 2L, 3L })
void testWithLongs(long argument) {
System.out.println(
"Parameterized test with (long) argument: " + argument);
assertNotNull(argument);
}
@ParameterizedTest
@ValueSource(doubles = { 4d, 5d })
void testWithDoubles(double argument) {
System.out.println(
"Parameterized test with (double) argument: " + argument);
assertNotNull(argument);
}
}
@ValueSource
3. Jupiter: el nuevo modelo de programación de JUnit 5
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
class EnumSourceParameterizedTest {
@ParameterizedTest
@EnumSource(TimeUnit.class)
void testWithEnum(TimeUnit argument) {
System.out.println(
"Parameterized test with (TimeUnit) argument: " + argument);
assertNotNull(argument);
}
}
@EnumSource
3. Jupiter: el nuevo modelo de programación de JUnit 5
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
class MethodSourceStringsParameterizedTest {
static Stream<String> stringProvider() {
return Stream.of("hello", "world");
}
@ParameterizedTest
@MethodSource("stringProvider")
void testWithStringProvider(String argument) {
System.out.println(
"Parameterized test with (String) argument: " + argument);
assertNotNull(argument);
}
}
@MethodSource
3. Jupiter: el nuevo modelo de programación de JUnit 5
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class CsvSourceParameterizedTest {
@ParameterizedTest
@CsvSource({ "hello, 1", "world, 2", "'happy, testing', 3" })
void testWithCsvSource(String first, int second) {
System.out.println("Parameterized test with (String) " + first
+ " and (int) " + second);
assertNotNull(first);
assertNotEquals(0, second);
}
}
@CsvSource
3. Jupiter: el nuevo modelo de programación de JUnit 5
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
class CsvFileSourceParameterizedTest {
@ParameterizedTest
@CsvFileSource(resources = "/input.csv")
void testWithCsvFileSource(String first, int second) {
System.out.println("Yet another parameterized test with (String) "
+ first + " and (int) " + second);
assertNotNull(first);
assertNotEquals(0, second);
}
}
@CsvFileSource
3. Jupiter: el nuevo modelo de programación de JUnit 5
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
class ArgumentSourceParameterizedTest {
@ParameterizedTest
@ArgumentsSource(CustomArgumentsProvider1.class)
void testWithArgumentsSource(String first, int second) {
System.out.println("Parameterized test with (String) "
+ first + " and (int) " + second);
assertNotNull(first);
assertTrue(second > 0);
}
}
@ArgumentsSource
import java.util.stream.Stream;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
public class CustomArgumentsProvider1 implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(
ExtensionContext context) {
System.out.println("Arguments provider [1] to test "
+ context.getTestMethod().get().getName());
return Stream.of(Arguments.of("hello", 1),
Arguments.of("world", 2));
}
}
3. Jupiter: el nuevo modelo de programación de JUnit 5
• A medio cambio entre los test parametrizados y el modelo de
extensión nos encontramos las plantillas de tests
• Los métodos anotados con @TestTemplate se ejecutarán múltiples
veces dependiendo de los valores devueltos por el proveedor de
contexto
• Las plantillas de test se usan siempre en conjunción con el punto de
extensión TestTemplateInvocationContextProvider
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(MyTestTemplateInvocationContextProvider.class)
class TemplateTest {
@TestTemplate
void testTemplate(String parameter) {
System.out.println(parameter);
}
}
En este caso deberíamos implementar la extensión
MyTestTemplateInvocationContextProvider
Contenidos
1. Introducción a las pruebas en el software
2. Motivación y arquitectura de JUnit 5
3. Jupiter: el nuevo modelo de programación de JUnit 5
4. Modelo de extensiones en JUnit 5
5. Selenium-Jupiter
6. Conclusiones
4. Modelo de extensiones en JUnit 5
• El modelo de extensión de JUnit 5 (también llamado Jupiter) permite
ampliar el modelo de programación con funcionalidades personalizadas
• Gracias al modelo de extensiones, frameworks externos pueden
proporcionar integración con JUnit 5 de una manera sencilla
• Hay 3 formas de usar una extensión en Jupiter:
1. Declarativamente, usando la anotación @ExtendWith (se puede usar a nivel
de clase o de método)
2. Programáticamente, usando la anotación @RegisterExtension (la
diferencia es que tenemos una instancia de la extensión disponible en la clase
de tests)
3. Automáticamente, usando el mecanismo de carga de servicios de Java a través
de la clase java.util.ServiceLoader
4. Modelo de extensiones en JUnit 5
1. Declarativamente (@ExtendWith):
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(MyExtension.class)
public class MyTest {
@Test
public void test1() {
// ...
}
@Test
public void test2() {
// ...
}
}
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
public class MyTest {
@ExtendWith(MyExtension.class)
@Test
public void test1() {
// ...
}
@Test
public void test2() {
// ...
}
}
Declarada a nivel de clase, la
extensión estará registrada
para todos los tests de la clase
Declarada a nivel de test, la
extensión estará registrada
únicamente para dicho test
4. Modelo de extensiones en JUnit 5
2. Programáticamente (@RegisterExtension)
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
public class MyTest {
@RegisterExtension
MyExtension myExtension = new MyExtension();
@Test
public void test1() {
// ...
}
@Test
public void test2() {
// ...
}
}
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
public class MyTest {
@RegisterExtension
static MyExtension myExtension = new MyExtension();
@Test
public void test1() {
// ...
}
@Test
public void test2() {
// ...
}
}
La instancia de la extensión registrada
mediante @RegisterExtension puede
ser usada programáticamente para
configuración de la extensión o en los
propios tests
No es obligatorio declarar la instancia como
estático, aunque de esta forma se limitan
los puntos de extensión a nivel de instancia
(BeforeEachCallback,
AfterEachCallback, etc.)
4. Modelo de extensiones en JUnit 5
3. Automáticamente (java.util.ServiceLoader)
• Para registrar una extensión mediante este mecanismo, en primer lugar hay
que declarar el nombre cualificado de la extensión en el siguiente fichero:
/META-INF/services/org.junit.jupiter.api.extension.Extension
• Después hay que pasar la siguiente propiedad de la JVM:
-Djunit.jupiter.extensions.autodetection.enabled=true
> mvn test -Djunit.jupiter.extensions.autodetection.enabled=true > gradle test -Djunit.jupiter.extensions.autodetection.enabled=true
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<properties>
<configurationParameters>
junit.jupiter.extensions.autodetection.enabled=true
</configurationParameters>
</properties>
</configuration>
</plugin>
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
testLogging.showStandardStreams = true
systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true'
}
4. Modelo de extensiones en JUnit 5
• Las extensiones en Jupiter se implementan haciendo uso de los
llamados puntos de extensión
• Los puntos de extensión son interfaces que permiten declarar cuatro
tipos de operaciones:
1. Añadir nueva lógica dentro del ciclo de vida de tests
2. Realizar ejecución condicional de tests
3. Realizar inyección de dependencias en métodos de tests
4. Gestionar las plantillas de test
4. Modelo de extensiones en JUnit 5
• Los puntos de extensión que controlan el ciclo de vida de tests son los
siguientes:
Punto de extensión
Implementadas por extensiones
que se ejecutarán…
TestInstancePostProcessor Justo después de la instanciación del test
BeforeAllCallback Antes de todos los tests de una clase
BeforeEachCallback Antes de cada test
BeforeTestExecutionCallback Justo antes de cada test
TestExecutionExceptionHandler Justo después de que ocurra una de
excepciones en el test
AfterTestExecutionCallback Justo después de cada test
AfterEachCallback Después de cada test
AfterAllCallback Después de todos los tests
4. Modelo de extensiones en JUnit 5
• El ciclo de vida completo teniendo en puntos de extensión y código de
usuario (@BeforeAll, @BeforeEach, @AfterEach, @AfterAll) es:
4. Modelo de extensiones en JUnit 5
• Ejemplo: extensión que ignora las excepciones de tipo
IOException
package io.github.bonigarcia;
import java.io.IOException;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
import org.junit.jupiter.api.extension.TestExtensionContext;
public class IgnoreIOExceptionExtension
implements TestExecutionExceptionHandler {
@Override
public void handleTestExecutionException(TestExtensionContext context,
Throwable throwable) throws Throwable {
if (throwable instanceof IOException) {
return;
}
throw throwable;
}
}
package io.github.bonigarcia;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
public class ExceptionTest {
@ExtendWith(IgnoreIOExceptionExtension.class)
@Test
public void test1() throws IOException {
throw new IOException("My IO Exception");
}
@Test
public void test2() throws IOException {
throw new IOException("My IO Exception");
}
}
En este ejemplo el primer
caso de prueba pasará
mientras el segundo
fallará
4. Modelo de extensiones en JUnit 5
• La ejecución condicional de tests se realiza mediante el punto de
extensión ExecutionCondition:
package io.github.bonigarcia;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
public class MyConditionalExtension implements ExecutionCondition {
@Override
public ConditionEvaluationResult evaluateExecutionCondition(
ExtensionContext arg0) {
boolean condition = ...
if (condition) {
return ConditionEvaluationResult.enabled("reason");
}
else {
return ConditionEvaluationResult.disabled("reason");
}
}
}
package io.github.bonigarcia;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(MyConditionalExtension.class)
class MyExtensionTest {
@Test
void test() {
// my test
}
}
En función de lo que
devuelva la extensión, los
tests se ejecutarán o serán
ignorados
4. Modelo de extensiones en JUnit 5
• La inyección de dependencias se realiza mediante el punto de
extensión ParameterResolver:
package io.github.bonigarcia;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
public class MyParameterResolver implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext)
throws ParameterResolutionException {
return true;
}
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext)
throws ParameterResolutionException {
return "my parameter";
}
}
package io.github.bonigarcia;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
public class MyTest {
@ExtendWith(MyParameterResolver.class)
@Test
public void test(Object parameter) {
System.out.println("---> parameter " + parameter);
}
} -------------------------------------------------------
T E S T S
-------------------------------------------------------
mar 13, 2017 5:37:36 PM
org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry
loadTestEngines
INFORMACIÓN: Discovered TestEngines with IDs: [junit-jupiter]
Running io.github.bonigarcia.MyTest
---> parameter my parameter
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed:
0.059 sec - in io.github.bonigarcia.MyTest
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
4. Modelo de extensiones en JUnit 5
• La gestión de plantillas de tests (@TestTemplate) se implementa
mediante el punto de extensión TestTemplateInvocationContextProvider
import java.util.stream.Stream;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
public class MyTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider {
@Override
public boolean supportsTestTemplate(ExtensionContext context) {
return true;
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ExtensionContext context) {
return Stream.of(invocationContext("test-1"), invocationContext("test-2"));
}
private TestTemplateInvocationContext invocationContext(String parameter) {
return new TestTemplateInvocationContext() {
@Override
public String getDisplayName(int invocationIndex) {
return parameter;
}
};
}
}
package io.github.bonigarcia;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
class TemplateTest {
@TestTemplate
@ExtendWith(MyTestTemplateInvocationContextProvider.class)
void testTemplate() {
}
}
4. Modelo de extensiones en JUnit 5
• Extensión de Mockito para JUnit 5 (pruebas unitarias):
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class LoginControllerLoginTest {
// Mocking objects
@InjectMocks
LoginController loginController;
@Mock
LoginService loginService;
// Test data
UserForm userForm = new UserForm("foo", "bar");
@Test
void testLoginOk() {
// Setting expectations (stubbing methods)
when(loginService.login(userForm)).thenReturn(true);
// Exercise SUT
String reseponseLogin = loginController.login(userForm);
// Verification
assertEquals("OK", reseponseLogin);
verify(loginService).login(userForm);
verifyNoMoreInteractions(loginService);
}
@Test
void testLoginKo() {
// Setting expectations (stubbing methods)
when(loginService.login(userForm)).thenReturn(false);
// Exercise SUT
String reseponseLogin = loginController.login(userForm);
// Verification
assertEquals("KO", reseponseLogin);
verify(loginService).login(userForm);
verifyZeroInteractions(loginService);
}
}
4. Modelo de extensiones en JUnit 5
• Extensión de Spring para JUnit 5 (pruebas de integración):
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { MySpringApplication.class })
class SimpleSpringTest {
@Autowired
public MessageComponent messageComponent;
@Test
public void test() {
assertEquals("Hello world!", messageComponent.getMessage());
}
}
spring
framework
4. Modelo de extensiones en JUnit 5
• Extensión de Spring para JUnit 5 (pruebas de integración):
spring
boot
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@SpringBootTest
class SimpleSpringBootTest {
@Autowired
public MessageComponent messageComponent;
@Test
public void test() {
assertEquals("Hello world!", messageComponent.getMessage());
}
}
4. Modelo de extensiones en JUnit 5
• Extensión de Selenium para JUnit 5 (pruebas end-to-end):
import static io.github.bonigarcia.BrowserType.CHROME;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import io.github.bonigarcia.DockerBrowser;
import io.github.bonigarcia.SeleniumExtension;
@ExtendWith(SeleniumExtension.class)
public class LocalWebDriverTest {
@Test
public void testLocalChrome(ChromeDriver driver) {
driver.get("https://bonigarcia.github.io/selenium-jupiter/");
assertTrue(chrome.getTitle().startsWith("Selenium-Jupiter"));
}
@Test
public void testDockerChrome(
@DockerBrowser(type = CHROME) RemoteWebDriver driver) {
driver.get("http://www.seleniumhq.org/");
assertTrue(firefox.getTitle().startsWith("Selenium"));
}
}
https://bonigarcia.github.io/selenium-jupiter/
4. Modelo de extensiones en JUnit 5
• Ejemplo completo:
• Aplicación web implementada con Spring-Boot
• Pruebas unitarias con Mockito
• Pruebas de integración con Spring
• Pruebas de sistema (e2e) con Selenium
• Ejecución de pruebas mediante Travis CI
• Análisis de código mediante SonarCloud
• Análisis de cobertura mediante Codedov https://github.com/bonigarcia/rate-my-cat
Contenidos
1. Introducción a las pruebas en el software
2. Motivación y arquitectura de JUnit 5
3. Jupiter: el nuevo modelo de programación de JUnit 5
4. Modelo de extensiones en JUnit 5
5. Selenium-Jupiter
6. Conclusiones
5. Selenium-Jupiter
• Selenium-Jupiter permite usar Selenium (WebDriver y Grid) desde casos
de prueba JUnit 5 de manera sencilla
• De forma interna, Selenium-Jupiter implementa tres puntos de
extensión de JUnit 5:
public class SeleniumExtension implements ParameterResolver, AfterEachCallback,
TestTemplateInvocationContextProvider {
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) {
// Logic for dependency injection
}
@Override
public void afterEach(ExtensionContext context) {
// Logic for clean resources after test
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ExtensionContext extensionContext) {
// Logic for support test templates
}
}
5. Selenium-Jupiter
• Selenium-Jupiter permite usar diferentes tipos de browser
1. Locales (instalados en la máquina que ejecuta el test, a través de Selenium
WebDirver)
2. Remotos (instalados en otras máquinas, a través de Selenium Grid)
3. En contenedores Docker (a través de contendores Docker y Selenium Grid)
• Para navegadores locales, la gestión de los binarios necesarios,
Selenium-Jupiter usa WebDriverManager
https://github.com/bonigarcia/webdrivermanager
5. Selenium-Jupiter
• Ejemplo de uso de navegadores locales:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import io.github.bonigarcia.SeleniumExtension;
@ExtendWith(SeleniumExtension.class)
public class ChromeAndFirefoxJupiterTest {
@Test
public void testWithOneChrome(ChromeDriver chromeDriver) {
// Use Chrome in this test
}
@Test
public void testWithFirefox(FirefoxDriver firefoxDriver) {
// Use Firefox in this test
}
@Test
public void testWithChromeAndFirefox(ChromeDriver chromeDriver,
FirefoxDriver firefoxDriver) {
// Use Chrome and Firefox in this test
}
}
Selenium-Jupiter se encarga de instanciar e
inyectar objetos de los siguientes tipos:
• ChromeDriver
• FirefoxDriver
• EdgeDriver
• OperaDriver
• SafariDriver
• HtmlUnitDriver
• PhantomJSDriver
• InternetExplorerDriver
• AppiumDriver
5. Selenium-Jupiter
• Ejemplo de uso de navegadores remotos:
import static org.openqa.selenium.remote.DesiredCapabilities.firefox;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import io.github.bonigarcia.DriverCapabilities;
import io.github.bonigarcia.DriverUrl;
import io.github.bonigarcia.SeleniumExtension;
@ExtendWith(SeleniumExtension.class)
public class RemoteWebDriverJupiterTest {
@DriverUrl
String url = "http://localhost:4444/wd/hub";
@DriverCapabilities
Capabilities capabilities = firefox();
@Test
void testWithRemoteChrome(@DriverUrl("http://localhost:4444/wd/hub")
@DriverCapabilities("browserName=chrome") RemoteWebDriver driver) {
// Use remote Chrome in this test
}
@Test
void testWithRemoteFirefox(RemoteWebDriver driver) {
// Use remote Firefox in this test
}
}
Selenium-Jupiter proporciona las
anotación @DriverUrl para definir la
URL del Selenium/Appium Server y
@DriverCapabilities para las
capacidades requeridas
5. Selenium-Jupiter
• Ejemplo de uso de navegadores en contenedores Docker:
import static io.github.bonigarcia.BrowserType.CHROME;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.remote.RemoteWebDriver;
import io.github.bonigarcia.DockerBrowser;
import io.github.bonigarcia.SeleniumExtension;
@ExtendWith(SeleniumExtension.class)
public class DockerChromeJupiterTest {
@Test
public void testChrome(
@DockerBrowser(type = CHROME) RemoteWebDriver driver) {
driver.get("https://bonigarcia.github.io/selenium-jupiter/");
assertThat(driver.getTitle(),
containsString("JUnit 5 extension for Selenium"));
}
@Test
public void testChromeWithVersion(@DockerBrowser(type = CHROME, version = "67.0")
RemoteWebDriver driver) {
driver.get("https://bonigarcia.github.io/selenium-jupiter/");
assertThat(driver.getTitle(),
containsString("JUnit 5 extension for Selenium"));
}
}
Selenium-Jupiter proporciona las
anotación @DockerBrowser para
declarar el uso de browsers en
contenedores Docker
Si no se especifica el parámetro,
version la última versión disponible
en Docker Hub se usará
Los tipos de browser soportados
actualmente son: CHROME, FIREFOX,
OPERA, y ANDROID
El parámetro version admite
además los siguiente valores
especiales: latest, latest-*,
beta, y unstable
5. Selenium-Jupiter
• Ejemplo de uso de navegadores en contenedores Docker:
import static io.github.bonigarcia.BrowserType.ANDROID;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.remote.RemoteWebDriver;
import io.github.bonigarcia.DockerBrowser;
import io.github.bonigarcia.SeleniumExtension;
@ExtendWith(SeleniumExtension.class)
public class DockerAndroidCustomJupiterTest {
@Test
public void testAndroid(@DockerBrowser(type = ANDROID, version = "5.0.1",
deviceName = "Nexus S", browserName = "browser") RemoteWebDriver driver) {
driver.get("https://bonigarcia.github.io/selenium-jupiter/");
assertThat(driver.getTitle(),
containsString("JUnit 5 extension for Selenium"));
}
} En el caso de dispositivos Android, a
demás de la versión, el tipo de
dispositivo y el navegador se puede
especificar
Android version API level Browser name
5.0.1 21 browser
5.1.1 22 browser
6.0 23 chrome
7.0 24 chrome
7.1.1 25 chrome
Type Device name
Phone Samsung Galaxy S6
Phone Nexus 4
Phone Nexus 5
Phone Nexus One
Phone Nexus S
Tablet Nexus 7
5. Selenium-Jupiter
• Al usar browsers en contendores Docker, es posible interactuar con la
sesión mediante VNC así como grabar dichas sesiones
5. Selenium-Jupiter
• Selenium-Jupiter hace uso del soporte de JUnit 5 de plantillas de test
• Se puede definir el número y tipo de browser:
1. Mediante un fichero JSON
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.WebDriver;
import io.github.bonigarcia.SeleniumExtension;
import io.github.bonigarcia.SeleniumJupiter;
@ExtendWith(SeleniumExtension.class)
public class TemplateTest {
@TestTemplate
void templateTest(WebDriver driver) {
driver.get("https://bonigarcia.github.io/selenium-jupiter/");
assertThat(driver.getTitle(),
containsString("JUnit 5 extension for Selenium"));
}
}
{
"browsers": [
[
{
"type": "chrome-in-docker",
"version": "latest"
}
],
[
{
"type": "chrome-in-docker",
"version": "latest-1"
}
],
[
{
"type": "chrome-in-docker",
"version": "beta"
}
],
[
{
"type": "chrome-in-docker",
"version": "unstable"
}
]
]
}
5. Selenium-Jupiter
• Selenium-Jupiter hace uso del soporte de JUnit 5 de plantillas de test
• Se puede definir el número y tipo de browser:
2. Programáticamente import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.openqa.selenium.WebDriver;
import io.github.bonigarcia.BrowserBuilder;
import io.github.bonigarcia.BrowsersTemplate.Browser;
import io.github.bonigarcia.SeleniumExtension;
public class TemplateRegisterTest {
@RegisterExtension
static SeleniumExtension seleniumExtension = new SeleniumExtension();
@BeforeAll
static void setup() {
Browser chrome = BrowserBuilder.chrome().build();
Browser firefox = BrowserBuilder.firefox().build();
seleniumExtension.addBrowsers(chrome);
seleniumExtension.addBrowsers(firefox);
}
@TestTemplate
void templateTest(WebDriver driver) {
// ...
}
}
5. Selenium-Jupiter
• Selenium-Jupiter permite el uso de browser genéricos,
especificados haciendo uso de parámetros de configuración
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.WebDriver;
import io.github.bonigarcia.SeleniumExtension;
@ExtendWith(SeleniumExtension.class)
public class GenericTest {
@Test
void genericTest(WebDriver driver) {
driver.get("https://bonigarcia.github.io/selenium-jupiter/");
assertThat(driver.getTitle(),
containsString("JUnit 5 extension for Selenium"));
}
}
El browser por defecto se especifica
mediante la clave de configuración
sel.jup.default.browser
(chrome-in-docker por defecto)
Se define una lista de browser alternativos
mediante la clave de configuración
sel.jup.default.browser.fallback
La configuración de Selenium-Jupiter también
puede hacerse programática a través del
método SeleniumJupiter.config()
5. Selenium-Jupiter
• Selenium-Jupiter se integra fácilmente con Jenkins
• Permite la exportación de ficheros (grabaciones, screenshots) a través
del plugin de adjuntos de Jenkins (Jenkins attachment plugin)
• Dando valor surefire-reports a la
clave de configuración
sel.jup.output.folder, Selenium-
Jupiter almacenará los ficheros
generados en los tests en la ruta
adecuada para que el plugin los
encuentre y los publique en la GUI de
Jenkins asociado a la ejecución del job
Contenidos
1. Introducción a las pruebas en el software
2. Motivación y arquitectura de JUnit 5
3. Jupiter: el nuevo modelo de programación de JUnit 5
4. Modelo de extensiones en JUnit 5
5. Selenium-Jupiter
6. Conclusiones
6. Conclusiones
• JUnit 5 ha supuesto el rediseño completo del framework JUnit
• Es modular y está formado por Jupiter, Vintage, Platform
• Ofrece tres tipos de APIs: Test API, Test Engine SPI, Test Launcher API
• El modelo de programación en JUnit 5 (Jupiter) presenta muchas
novedades con respecto a JUnit 4
• Jupiter puede crecer mediante un modelo de extensiones basado en
diferentes puntos de extensión: ciclo de vida, ejecución condicional,
inyección de dependencias, plantillas de test
• La versión 5.3 de JUnit 5 está actualmente en desarrollo. En esta
versión se centra en la ejecución paralela de tests, escenarios de test
(organización de unidades de test), etc.
6. Conclusiones
• Algunas features de JUnit 5 que no hemos visto en esta charla:
• Test anidados (@Nested)
• Test dinámicos (@TestFactory)
• Soporte de reglas de JUnit 4 en JUnit 5
• Uso de interfaces y métodos por defecto para tests
• Compatibilidad de JUnit 5 y Java 9
• Tampoco hemos visto la integración de JUnit 5 con:
• Cucumber (hay un extensión propuesta y quieren hacer un test engine)
• Docker (hay una extensión que permite ejecutar contenedores Dockers desde
casos de prueba JUnit 5)
• Android (plugin Gradle para ejecutar tests JUnit 5)
• Servicios REST (con Spring, REST Assured, o WireMock)
• Otros: integración continua, ciclo de vida del desarrollo, requisitos…
Introducción y novedades de
JUnit 5
Muchas gracias
Boni García
boni.garcia@urjc.es http://bonigarcia.github.io/
@boni_gg https://github.com/bonigarcia

Weitere ähnliche Inhalte

Was ist angesagt?

Unit Testing with Jest
Unit Testing with JestUnit Testing with Jest
Unit Testing with JestMaayan Glikser
 
JUnit- A Unit Testing Framework
JUnit- A Unit Testing FrameworkJUnit- A Unit Testing Framework
JUnit- A Unit Testing FrameworkOnkar Deshpande
 
Run your Appium tests using Docker Android - AppiumConf 2019
Run your Appium tests using Docker Android - AppiumConf 2019Run your Appium tests using Docker Android - AppiumConf 2019
Run your Appium tests using Docker Android - AppiumConf 2019Sargis Sargsyan
 
Kotlin Basics & Introduction to Jetpack Compose.pptx
Kotlin Basics & Introduction to Jetpack Compose.pptxKotlin Basics & Introduction to Jetpack Compose.pptx
Kotlin Basics & Introduction to Jetpack Compose.pptxtakshilkunadia
 
Parallel Test Runs with Appium on Real Mobile Devices – Hands-on Webinar
Parallel Test Runs with Appium on Real Mobile Devices – Hands-on WebinarParallel Test Runs with Appium on Real Mobile Devices – Hands-on Webinar
Parallel Test Runs with Appium on Real Mobile Devices – Hands-on WebinarBitbar
 
What is JUnit? | Edureka
What is JUnit? | EdurekaWhat is JUnit? | Edureka
What is JUnit? | EdurekaEdureka!
 
Visualization for Software Analytics
Visualization for Software AnalyticsVisualization for Software Analytics
Visualization for Software AnalyticsMargaret-Anne Storey
 
Шаблоны разработки ПО. Часть 2. ООП и UML
Шаблоны разработки ПО. Часть 2. ООП и UMLШаблоны разработки ПО. Часть 2. ООП и UML
Шаблоны разработки ПО. Часть 2. ООП и UMLSergey Nemchinsky
 
Jetpack Compose.pptx
Jetpack Compose.pptxJetpack Compose.pptx
Jetpack Compose.pptxGDSCVJTI
 
[Webinar] Qt Test-Driven Development Using Google Test and Google Mock
[Webinar] Qt Test-Driven Development Using Google Test and Google Mock[Webinar] Qt Test-Driven Development Using Google Test and Google Mock
[Webinar] Qt Test-Driven Development Using Google Test and Google MockICS
 
TestLink introduction
TestLink introductionTestLink introduction
TestLink introductionDavid Ionut
 

Was ist angesagt? (20)

Software test life cycle
Software test life cycleSoftware test life cycle
Software test life cycle
 
Unit Testing with Jest
Unit Testing with JestUnit Testing with Jest
Unit Testing with Jest
 
Junit
JunitJunit
Junit
 
JUnit- A Unit Testing Framework
JUnit- A Unit Testing FrameworkJUnit- A Unit Testing Framework
JUnit- A Unit Testing Framework
 
Sikuli Slides
Sikuli SlidesSikuli Slides
Sikuli Slides
 
Run your Appium tests using Docker Android - AppiumConf 2019
Run your Appium tests using Docker Android - AppiumConf 2019Run your Appium tests using Docker Android - AppiumConf 2019
Run your Appium tests using Docker Android - AppiumConf 2019
 
Java codes
Java codesJava codes
Java codes
 
Java modules
Java modulesJava modules
Java modules
 
Kotlin Basics & Introduction to Jetpack Compose.pptx
Kotlin Basics & Introduction to Jetpack Compose.pptxKotlin Basics & Introduction to Jetpack Compose.pptx
Kotlin Basics & Introduction to Jetpack Compose.pptx
 
Parallel Test Runs with Appium on Real Mobile Devices – Hands-on Webinar
Parallel Test Runs with Appium on Real Mobile Devices – Hands-on WebinarParallel Test Runs with Appium on Real Mobile Devices – Hands-on Webinar
Parallel Test Runs with Appium on Real Mobile Devices – Hands-on Webinar
 
Introduction to JUnit
Introduction to JUnitIntroduction to JUnit
Introduction to JUnit
 
What is JUnit? | Edureka
What is JUnit? | EdurekaWhat is JUnit? | Edureka
What is JUnit? | Edureka
 
Visualization for Software Analytics
Visualization for Software AnalyticsVisualization for Software Analytics
Visualization for Software Analytics
 
Шаблоны разработки ПО. Часть 2. ООП и UML
Шаблоны разработки ПО. Часть 2. ООП и UMLШаблоны разработки ПО. Часть 2. ООП и UML
Шаблоны разработки ПО. Часть 2. ООП и UML
 
Unit testing with JUnit
Unit testing with JUnitUnit testing with JUnit
Unit testing with JUnit
 
Java 9 Features
Java 9 FeaturesJava 9 Features
Java 9 Features
 
TestNG Framework
TestNG Framework TestNG Framework
TestNG Framework
 
Jetpack Compose.pptx
Jetpack Compose.pptxJetpack Compose.pptx
Jetpack Compose.pptx
 
[Webinar] Qt Test-Driven Development Using Google Test and Google Mock
[Webinar] Qt Test-Driven Development Using Google Test and Google Mock[Webinar] Qt Test-Driven Development Using Google Test and Google Mock
[Webinar] Qt Test-Driven Development Using Google Test and Google Mock
 
TestLink introduction
TestLink introductionTestLink introduction
TestLink introduction
 

Ähnlich wie JUnit 5 Intro

Introducción y novedades de JUnit 5 (16/01/2018)
Introducción y novedades de JUnit 5 (16/01/2018)Introducción y novedades de JUnit 5 (16/01/2018)
Introducción y novedades de JUnit 5 (16/01/2018)Boni García
 
Pruebas software con junit ..
Pruebas software con junit ..Pruebas software con junit ..
Pruebas software con junit ..siticfje
 
Pruebas Unitarias
Pruebas UnitariasPruebas Unitarias
Pruebas Unitariasggarber
 
Desambiguación del Término - Pruebas Unitarias - por Jorge H. Abad abad L.
Desambiguación del Término -  Pruebas Unitarias - por Jorge H. Abad abad L.Desambiguación del Término -  Pruebas Unitarias - por Jorge H. Abad abad L.
Desambiguación del Término - Pruebas Unitarias - por Jorge H. Abad abad L.Jorge Hernán Abad Londoño
 
Tests Unitarios con JUnit 4
Tests Unitarios con JUnit 4Tests Unitarios con JUnit 4
Tests Unitarios con JUnit 4Paulo Clavijo
 
¿Cómo poner software de calidad en manos del usuario de forma rápida?
¿Cómo poner software de calidad en manos del usuario de forma rápida?¿Cómo poner software de calidad en manos del usuario de forma rápida?
¿Cómo poner software de calidad en manos del usuario de forma rápida?Micael Gallego
 
Cascada vs Agile Scrum v2.0
Cascada vs Agile Scrum v2.0Cascada vs Agile Scrum v2.0
Cascada vs Agile Scrum v2.0TestingBaires
 
Fundamento pruebas Ingeniería del software
Fundamento pruebas Ingeniería del softwareFundamento pruebas Ingeniería del software
Fundamento pruebas Ingeniería del softwareWilliam Remolina
 
Pruebas Unitarias - Uso de NUnit dentro de proyectos .NET
Pruebas Unitarias - Uso de NUnit dentro de proyectos .NETPruebas Unitarias - Uso de NUnit dentro de proyectos .NET
Pruebas Unitarias - Uso de NUnit dentro de proyectos .NETLa Red DBAccess
 
Test Automation .NET
Test Automation .NETTest Automation .NET
Test Automation .NETAngel Nuñez
 
Cursotdd 141202105217-conversion-gate01
Cursotdd 141202105217-conversion-gate01Cursotdd 141202105217-conversion-gate01
Cursotdd 141202105217-conversion-gate01Javier Morales
 
Taller de Simpletest - Drupal Day Valencia 2012
Taller de Simpletest - Drupal Day Valencia 2012Taller de Simpletest - Drupal Day Valencia 2012
Taller de Simpletest - Drupal Day Valencia 2012Juampy NR
 

Ähnlich wie JUnit 5 Intro (20)

Introducción y novedades de JUnit 5 (16/01/2018)
Introducción y novedades de JUnit 5 (16/01/2018)Introducción y novedades de JUnit 5 (16/01/2018)
Introducción y novedades de JUnit 5 (16/01/2018)
 
Pruebas software con junit ..
Pruebas software con junit ..Pruebas software con junit ..
Pruebas software con junit ..
 
Pruebas Unitarias
Pruebas UnitariasPruebas Unitarias
Pruebas Unitarias
 
Presentación: xUnit y Junit
Presentación: xUnit y JunitPresentación: xUnit y Junit
Presentación: xUnit y Junit
 
Pruebas unitarias
Pruebas unitariasPruebas unitarias
Pruebas unitarias
 
Desambiguación del Término - Pruebas Unitarias - por Jorge H. Abad abad L.
Desambiguación del Término -  Pruebas Unitarias - por Jorge H. Abad abad L.Desambiguación del Término -  Pruebas Unitarias - por Jorge H. Abad abad L.
Desambiguación del Término - Pruebas Unitarias - por Jorge H. Abad abad L.
 
J unit4
J unit4J unit4
J unit4
 
Practicas tecnicas
Practicas tecnicasPracticas tecnicas
Practicas tecnicas
 
Tests Unitarios con JUnit 4
Tests Unitarios con JUnit 4Tests Unitarios con JUnit 4
Tests Unitarios con JUnit 4
 
Esquemas de pruebas
Esquemas de pruebasEsquemas de pruebas
Esquemas de pruebas
 
Las mejores herramientas para realizar pruebas de software
Las mejores herramientas para realizar pruebas de softwareLas mejores herramientas para realizar pruebas de software
Las mejores herramientas para realizar pruebas de software
 
¿Cómo poner software de calidad en manos del usuario de forma rápida?
¿Cómo poner software de calidad en manos del usuario de forma rápida?¿Cómo poner software de calidad en manos del usuario de forma rápida?
¿Cómo poner software de calidad en manos del usuario de forma rápida?
 
Cascada vs Agile Scrum v2.0
Cascada vs Agile Scrum v2.0Cascada vs Agile Scrum v2.0
Cascada vs Agile Scrum v2.0
 
U2T4 - Pruebas del Software
U2T4 - Pruebas del SoftwareU2T4 - Pruebas del Software
U2T4 - Pruebas del Software
 
Fundamento pruebas Ingeniería del software
Fundamento pruebas Ingeniería del softwareFundamento pruebas Ingeniería del software
Fundamento pruebas Ingeniería del software
 
Pruebas Unitarias - Uso de NUnit dentro de proyectos .NET
Pruebas Unitarias - Uso de NUnit dentro de proyectos .NETPruebas Unitarias - Uso de NUnit dentro de proyectos .NET
Pruebas Unitarias - Uso de NUnit dentro de proyectos .NET
 
Test Automation .NET
Test Automation .NETTest Automation .NET
Test Automation .NET
 
Cursotdd 141202105217-conversion-gate01
Cursotdd 141202105217-conversion-gate01Cursotdd 141202105217-conversion-gate01
Cursotdd 141202105217-conversion-gate01
 
GXUnit
GXUnitGXUnit
GXUnit
 
Taller de Simpletest - Drupal Day Valencia 2012
Taller de Simpletest - Drupal Day Valencia 2012Taller de Simpletest - Drupal Day Valencia 2012
Taller de Simpletest - Drupal Day Valencia 2012
 

Mehr von Boni García

Selenium Manager: Automated Driver & Browser Management for Selenium WebDriver
Selenium Manager: Automated Driver & Browser Management for Selenium WebDriverSelenium Manager: Automated Driver & Browser Management for Selenium WebDriver
Selenium Manager: Automated Driver & Browser Management for Selenium WebDriverBoni García
 
WebDriverManager: the Swiss Army Knife for Selenium WebDriver
WebDriverManager: the Swiss Army Knife for Selenium WebDriverWebDriverManager: the Swiss Army Knife for Selenium WebDriver
WebDriverManager: the Swiss Army Knife for Selenium WebDriverBoni García
 
Developing Selenium tests with JUnit 5
Developing Selenium tests with JUnit 5Developing Selenium tests with JUnit 5
Developing Selenium tests with JUnit 5Boni García
 
Toolbox for Selenium Tests in Java: WebDriverManager and Selenium-Jupiter
Toolbox for Selenium Tests in Java: WebDriverManager and Selenium-JupiterToolbox for Selenium Tests in Java: WebDriverManager and Selenium-Jupiter
Toolbox for Selenium Tests in Java: WebDriverManager and Selenium-JupiterBoni García
 
Extending WebDriver: A cloud approach
Extending WebDriver: A cloud approachExtending WebDriver: A cloud approach
Extending WebDriver: A cloud approachBoni García
 
A Proposal to Orchestrate Test Cases
A Proposal to Orchestrate Test CasesA Proposal to Orchestrate Test Cases
A Proposal to Orchestrate Test CasesBoni García
 
User Impersonation as a Service in End-to-End Testing
User Impersonation as a Service in End-to-End TestingUser Impersonation as a Service in End-to-End Testing
User Impersonation as a Service in End-to-End TestingBoni García
 
WebRTC Testing: State of the Art
WebRTC Testing: State of the ArtWebRTC Testing: State of the Art
WebRTC Testing: State of the ArtBoni García
 
ElasTest: an elastic platform for testing complex distributed large software ...
ElasTest: an elastic platform for testing complex distributed large software ...ElasTest: an elastic platform for testing complex distributed large software ...
ElasTest: an elastic platform for testing complex distributed large software ...Boni García
 
Analysis of video quality and end-to-end latency in WebRTC
Analysis of video quality and end-to-end latency in WebRTCAnalysis of video quality and end-to-end latency in WebRTC
Analysis of video quality and end-to-end latency in WebRTCBoni García
 
NUBOMEDIA: an Elastic PaaS Enabling the Convergence of Real-Time and Big Data...
NUBOMEDIA: an Elastic PaaS Enabling the Convergence of Real-Time and Big Data...NUBOMEDIA: an Elastic PaaS Enabling the Convergence of Real-Time and Big Data...
NUBOMEDIA: an Elastic PaaS Enabling the Convergence of Real-Time and Big Data...Boni García
 
WebRTC/Kurento/NUBOMEDIA Hackathon at IETF’96
WebRTC/Kurento/NUBOMEDIA Hackathon at IETF’96WebRTC/Kurento/NUBOMEDIA Hackathon at IETF’96
WebRTC/Kurento/NUBOMEDIA Hackathon at IETF’96Boni García
 
Cloud Instances of Kurento v6 on FIWARE Lab
Cloud Instances of Kurento v6 on FIWARE LabCloud Instances of Kurento v6 on FIWARE Lab
Cloud Instances of Kurento v6 on FIWARE LabBoni García
 
Kurento v6 Development Guide
Kurento v6 Development GuideKurento v6 Development Guide
Kurento v6 Development GuideBoni García
 
Kurento v6 Installation Guide
Kurento v6 Installation GuideKurento v6 Installation Guide
Kurento v6 Installation GuideBoni García
 
Introduction to the Stream Oriented GE (Kurento v6)
Introduction to the Stream Oriented GE (Kurento v6)Introduction to the Stream Oriented GE (Kurento v6)
Introduction to the Stream Oriented GE (Kurento v6)Boni García
 

Mehr von Boni García (17)

Selenium Manager: Automated Driver & Browser Management for Selenium WebDriver
Selenium Manager: Automated Driver & Browser Management for Selenium WebDriverSelenium Manager: Automated Driver & Browser Management for Selenium WebDriver
Selenium Manager: Automated Driver & Browser Management for Selenium WebDriver
 
WebDriverManager: the Swiss Army Knife for Selenium WebDriver
WebDriverManager: the Swiss Army Knife for Selenium WebDriverWebDriverManager: the Swiss Army Knife for Selenium WebDriver
WebDriverManager: the Swiss Army Knife for Selenium WebDriver
 
Developing Selenium tests with JUnit 5
Developing Selenium tests with JUnit 5Developing Selenium tests with JUnit 5
Developing Selenium tests with JUnit 5
 
Toolbox for Selenium Tests in Java: WebDriverManager and Selenium-Jupiter
Toolbox for Selenium Tests in Java: WebDriverManager and Selenium-JupiterToolbox for Selenium Tests in Java: WebDriverManager and Selenium-Jupiter
Toolbox for Selenium Tests in Java: WebDriverManager and Selenium-Jupiter
 
Extending WebDriver: A cloud approach
Extending WebDriver: A cloud approachExtending WebDriver: A cloud approach
Extending WebDriver: A cloud approach
 
A Proposal to Orchestrate Test Cases
A Proposal to Orchestrate Test CasesA Proposal to Orchestrate Test Cases
A Proposal to Orchestrate Test Cases
 
User Impersonation as a Service in End-to-End Testing
User Impersonation as a Service in End-to-End TestingUser Impersonation as a Service in End-to-End Testing
User Impersonation as a Service in End-to-End Testing
 
WebRTC Testing: State of the Art
WebRTC Testing: State of the ArtWebRTC Testing: State of the Art
WebRTC Testing: State of the Art
 
ElasTest: an elastic platform for testing complex distributed large software ...
ElasTest: an elastic platform for testing complex distributed large software ...ElasTest: an elastic platform for testing complex distributed large software ...
ElasTest: an elastic platform for testing complex distributed large software ...
 
Analysis of video quality and end-to-end latency in WebRTC
Analysis of video quality and end-to-end latency in WebRTCAnalysis of video quality and end-to-end latency in WebRTC
Analysis of video quality and end-to-end latency in WebRTC
 
NUBOMEDIA: an Elastic PaaS Enabling the Convergence of Real-Time and Big Data...
NUBOMEDIA: an Elastic PaaS Enabling the Convergence of Real-Time and Big Data...NUBOMEDIA: an Elastic PaaS Enabling the Convergence of Real-Time and Big Data...
NUBOMEDIA: an Elastic PaaS Enabling the Convergence of Real-Time and Big Data...
 
NUBOMEDIA Webinar
NUBOMEDIA WebinarNUBOMEDIA Webinar
NUBOMEDIA Webinar
 
WebRTC/Kurento/NUBOMEDIA Hackathon at IETF’96
WebRTC/Kurento/NUBOMEDIA Hackathon at IETF’96WebRTC/Kurento/NUBOMEDIA Hackathon at IETF’96
WebRTC/Kurento/NUBOMEDIA Hackathon at IETF’96
 
Cloud Instances of Kurento v6 on FIWARE Lab
Cloud Instances of Kurento v6 on FIWARE LabCloud Instances of Kurento v6 on FIWARE Lab
Cloud Instances of Kurento v6 on FIWARE Lab
 
Kurento v6 Development Guide
Kurento v6 Development GuideKurento v6 Development Guide
Kurento v6 Development Guide
 
Kurento v6 Installation Guide
Kurento v6 Installation GuideKurento v6 Installation Guide
Kurento v6 Installation Guide
 
Introduction to the Stream Oriented GE (Kurento v6)
Introduction to the Stream Oriented GE (Kurento v6)Introduction to the Stream Oriented GE (Kurento v6)
Introduction to the Stream Oriented GE (Kurento v6)
 

JUnit 5 Intro

  • 1. Introducción y novedades de JUnit 5 Universidad Rey Juan Carlos 04/07/2018 Boni García boni.garcia@urjc.es http://bonigarcia.github.io/ @boni_gg https://github.com/bonigarcia
  • 2. Boni García • Soy doctor en sistemas telemáticos por la Universidad Politécnica de Madrid (UPM) desde 2011 • Tesis doctoral centrada en las pruebas en el software (testing) • Actualmente trabajo como: • Investigador en la Universidad Rey Juan Carlos (URJC) • Profesor en el Centro Universitario de Tecnología y Arte Digital (U-tad) • Participo activamente en múltiples proyectos open source: • Comunidades: ElasTest, Kurento • Proyectos propios: WebDriverManager, Selenium-Jupiter, DualSub • Soy autor del libro Mastering Software Testing with JUnit 5 Pack Publishing (octubre 2017)
  • 3. Contenidos 1. Introducción a las pruebas en el software 2. Motivación y arquitectura de JUnit 5 3. Jupiter: el nuevo modelo de programación de JUnit 5 4. Modelo de extensiones en JUnit 5 5. Selenium-Jupiter 6. Conclusiones
  • 4. 1. Introducción a las pruebas en el software • La calidad en el software es un término bastante ambiguo en el mundo de la ingeniería de software. Según Roger Pressman: Software quality is an effective software process applied in a manner that creates a useful product that provides measurable value for those who produce it and those who use it • El conjunto de técnicas destinadas a asegurar la calidad (QA, Quality Assurance) en un proyecto software se conocen como Verificación y Validación (V&V, Verification & Validation). Según Barry Boehm: Are we building the product right? (verification) Are we building the right product? (validation)
  • 5. 1. Introducción a las pruebas en el software • Hay dos tipos de técnicas dentro de V&V: 1. Análisis (estático): evaluación del software (ya sea código, modelos, documentación, etc.) sin ejecutar el propio software • Revisión de pares: Collaborator, Crucible, Gerrit … • Análisis automático (linters): SonarQube, Checkstyle, FindBugs, PMD … 2. Pruebas (dinámico): evaluación del software observando un resultado esperado de la ejecución de una parte o todo el sistema (caso de prueba) y dar un veredicto sobre la misma
  • 6. 1. Introducción a las pruebas en el software Test Level Unit Integration System Acceptance Method Black-box White-box Non- functional Type Manual Automated • Pruebas unitarias (componentes aislados) • Pruebas de integración (diferentes componentes) • Pruebas de sistema (todos los componentes) • Pruebas de aceptación (pruebas de usuario) • Pruebas de caja negra (funcionales) • Pruebas de caja blanca (estructurales) • Pruebas no funcionales (rendimiento, seguridad,…) • Pruebas manuales • Pruebas automáticas (frameworks de prueba)
  • 7. 1. Introducción a las pruebas en el software • Las pruebas automáticas según Elfriede Dustin: Application and implementation of software technology throughout the entire software testing life cycle with the goal to improve efficiencies and effectiveness • Las pruebas automáticas son más efectivas cuando se implementan en base a un framework. Según Martin Fowler: A library is essentially a set of functions that you can call, these days usually organized into classes. Each call does some work and returns control to the client. A framework embodies some abstract design, with more behavior built in. In order to use it you need to insert your behavior into various places in the framework either by subclassing or by plugging in your own classes. The framework's code then calls your code at these points. Library Your code Framework contains callscalls
  • 8. 1. Introducción a las pruebas en el software Acceptance System Integration Unit Development testing (verification) User testing (validation) • Pirámide de tests:
  • 9. Contenidos 1. Introducción a las pruebas en el software 2. Motivación y arquitectura de JUnit 5 3. Jupiter: el nuevo modelo de programación de JUnit 5 4. Modelo de extensiones en JUnit 5 5. Selenium-Jupiter 6. Conclusiones
  • 10. 2. Motivación y arquitectura de JUnit 5 • JUnit es el framework más utilizado en la comunidad Java y uno de los más influyentes en la ingeniería de software en general (precursor de la familia xUnit) • La última versión de JUnit 4 fue liberada en diciembre de 2014 • JUnit 4 tiene importantes limitaciones que han propiciado el rediseño completo del framework en JUnit 5 The Top 100 Java libraries on GitHub (by OverOps)
  • 11. 2. Motivación y arquitectura de JUnit 5 • Los principales inconvenientes de JUnit 4 son: 1. JUnit 4 es monolítico. Toda las funciones de JUnit 4 son proporcionadas por un único componente, de forma que mecanismos como el descubrimiento de tests y la ejecución de los mismos están muy acoplados junit.jar Tests IDEs Build tools 3rd party frameworks Extensions
  • 12. 2. Motivación y arquitectura de JUnit 5 • Los principales inconvenientes de JUnit 4 son: 2. Los casos de prueba se ejecutan en JUnit 4 mediante unas clases especiales llamadas Test Runners. Estos runners tienen una limitación fundamental: no se pueden componer import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class MyTest1 { @Test public void myTest() { // my test code } } import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) public class MyTest2 { @Test public void myTest() { // my test code } }
  • 13. 2. Motivación y arquitectura de JUnit 5 • Los principales inconvenientes de JUnit 4 son: 3. Para mejorar la gestión del ciclo de vida de tests en JUnit 4 se desarrollaron las reglas (Test Rules), implementadas con las anotaciones @Rule y @ClassRule. El inconveniente es que puede llegar a ser complicado gestionar ambos ámbitos de forma simultánea (runners y rules) import org.junit.Rule; import org.junit.Test; import org.junit.rules.ErrorCollector; public class MyTest3 { @Rule public ErrorCollector errorCollector = new ErrorCollector(); @Test public void myTest() { // my test code } } import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TemporaryFolder; public class MyTest4 { @ClassRule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void myTest() { // my test code } }
  • 14. 2. Motivación y arquitectura de JUnit 5 • Para intentar solucionar estos problemas, en julio de 2015 Johannes Link y Mark Philipp pusieron en marcha una campaña para recaudar fondos y crear una nueva versión de JUnit • Esta campaña se conoció como JUnit Lambda crowdfunding campaign • Gracias a esta campaña se puso en marcha el equipo de JUnit 5, con miembros de diferentes compañías (Eclipse, Gradle, e IntelliJ entre otras)
  • 15. 2. Motivación y arquitectura de JUnit 5 • Arquitectura de JUnit 5: La plataforma JUnit (Platform) es un componente que actúa de ejecutor genérico para pruebas que se ejecutan en la JVM Test de versiones anteriores de JUnit (3 y 4) serán ejecutados a través del componente Vintage Jupiter es componente que implementa el nuevo modelo de programación y extensión en JUnit 5 La idea es que otros frameworks (e.g. Spock, Cucumber) pueden ejecutar sus propios casos de prueba realizando una extensión de la plataforma Los cliente programáticos usan la plataforma para el descubrimiento y la ejecución de los tests
  • 16. 2. Motivación y arquitectura de JUnit 5 • Hay tres tipos de módulos: 1. Test API: Módulos usados por testers para implementar casos de prueba 2. Test Engine SPI: Módulos extendidos para un framework de pruebas Java para la ejecución de un modelo concreto de tests 3. Test Launcher API: Módulos usados por clientes programáticos para el descubrimiento y ejecución de tests
  • 17. Contenidos 1. Introducción a las pruebas en el software 2. Motivación y arquitectura de JUnit 5 3. Jupiter: el nuevo modelo de programación de JUnit 5 4. Modelo de extensiones en JUnit 5 5. Selenium-Jupiter 6. Conclusiones
  • 18. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Los tests básicos en Jupiter son muy parecidos a JUnit 4: import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class BasicJUnit4Test { @BeforeClass public static void setupAll() { // setup all tests } @Before public void setup() { // setup each test } @Test public void test() { // exercise and verify SUT } @After public void teardown() { // teardown each test } @AfterClass public static void teardownAll() { // teardown all tests } }
  • 19. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Los tests básicos en Jupiter son muy parecidos a JUnit 4: import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class BasicJUnit5Test { @BeforeAll static void setupAll() { // setup all tests } @BeforeEach void setup() { // setup each test } @Test void test() { // exercise and verify SUT } @AfterEach void teardown() { // teardown each test } @AfterAll static void teardownAll() { // teardown all tests } } El nombre de las anotaciones que gestionan el ciclo de vida básico de los tests ha cambiado en Jupiter Se elimina la necesidad que las clases y los métodos de prueba tengan que ser public
  • 20. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Podemos ver el ciclo de vida de un caso de prueba en JUnit 5 de la siguiente manera:
  • 21. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Actualmente se pueden ejecutar test de JUnit 5 de diferentes formas: 1. Mediante herramienta de gestión y construcción de proyectos Java (build tools) • Maven • Gradle • Ant 2. Mediante un Entorno de Desarrollo Integrado (IDE) • IntelliJ IDEA 2016.2+ • Eclipse 4.7+ • Visual Studio (Java Extension Pack, enero 2018) 3. Mediante una herramienta propia de JUnit 5 • Console Launcher (standalone jar que permite ejecutar tests JUnit 5) java -jar junit-platform-console-standalone-version.jar <Options>
  • 22. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • En Maven, la gestión de proyectos Java se realiza a través de ciclos de vida independientes: • Clean: Para limpiar el proyecto • Default: Para la gestión propiamente dicha • Site: Para la documentación • Cada ciclo de vida está compuesto por fases que se ejecutan en cascada (compilar, empaquetar, ejecutar las pruebas, desplegar, etc.): Clean Lifecycle pre-clean clean post-clean Default Lifecycle validate generate-test-sources package initialize process-test-sources pre-integration-test generate-sources generate-test-resources integration-test process-sources process-test-resources post-integration-test generate-resources test-compile verify process-resources process-test-classes install compile test deploy process-classes prepare-package Site Lifecycle pre-site site post-site site-deploy Se usa la línea de comandos para invocar la ejecución de un ciclo de vida hasta una determinada fase. Ejemplo: mvn package
  • 23. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Cada fase está formada por objetivos (goals) • Los objetivos pueden ser específicos del tipo de empaquetado del proyecto (packaging) • Los objetivos son ejecutados por diferentes plugins Clean Lifecycle Fase Objetivo clean clean:clean Default Lifecycle (Packaging jar) Fase Objetivo process-resources resources:resources compile compiler:compile process-test-resources resources:testResources test-compile compiler:testCompile test surefire:test package jar:jar install install:install deploy deploy:deploy Clean Lifecycle Fase Objetivo site site:site site-deploy site:deploy
  • 24. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Los plugins y las versiones por defecto en los diferentes ciclos de vida se definen en un fichero llamado default-bindings.xml que está incluido en la distribución binaria de Maven • Por ejemplo, para el empaquetado jar del ciclo de vida por defecto: Default Lifecycle (Packaging jar) Fase Objetivo Plugin (artifactId) Version process-resources resources:resources maven-resources-plugin 2.6 compile compiler:compile maven-compiler-plugin 3.1 process-test-resources resources:testResources maven-resources-plugin 2.6 test-compile compiler:testCompile maven-compiler-plugin 3.1 test surefire:test maven-surefire-plugin 2.12.4 package jar:jar maven-jar-plugin 2.4 install install:install maven-install-plugin 2.4 deploy deploy:deploy maven-deploy-plugin 2.7 El groupId de todos estos plugins es org.apache.maven. plugins
  • 25. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Para las pruebas, nos interesan dos plugins de Maven: 1. maven-surefire-plugin: Plugin Maven por defecto para ejecutar las pruebas antes del empaquetado (teóricamente unitarias) > mvn test [INFO] Scanning for projects... [INFO] [INFO] ----------------< io.github.bonigarcia:junit5-failsafe >---------------- [INFO] Building junit5-failsafe 1.0.0 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ junit5-failsafe --- [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ junit5-failsafe --- [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ junit5-failsafe --- [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ junit5-failsafe --- [INFO] --- maven-surefire-plugin:2.22.0:test (default-test) @ junit5-failsafe --- [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running io.github.bonigarcia.ExecutedBySurefirePluginTest [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.016 s - in io.github.bonigarcia.ExecutedBySurefirePluginTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 7.423 s [INFO] Finished at: 2018-07-02T16:44:20+02:00 [INFO] ------------------------------------------------------------------------
  • 26. 3. Jupiter: el nuevo modelo de programación de JUnit 5 2. maven-failsafe-plugin: Plugin Maven para ejecutar las pruebas posteriores al empaquetado (teóricamente de integración) > mvn verify [INFO] Scanning for projects... [INFO] [INFO] ----------------< io.github.bonigarcia:junit5-failsafe >---------------- [INFO] Building junit5-failsafe 1.0.0 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ junit5-failsafe --- [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ junit5-failsafe --- [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ junit5-failsafe --- [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ junit5-failsafe --- [INFO] --- maven-surefire-plugin:2.22.0:test (default-test) @ junit5-failsafe --- [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ junit5-failsafe --- [INFO] --- maven-failsafe-plugin:2.22.0:integration-test (default) @ junit5-failsafe --- [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running io.github.bonigarcia.ExecutedByFailsafePluginIT [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.009 s - in io.github.bonigarcia.ExecutedByFailsafePluginIT [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] [INFO] --- maven-failsafe-plugin:2.22.0:verify (default) @ junit5-failsafe --- [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.887 s [INFO] Finished at: 2018-07-02T16:42:39+02:00 [INFO] ------------------------------------------------------------------------
  • 27. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • La siguiente tabla resume las principales diferencias entre Surefire y Failsafe : Surefire Failsafe Es un plugin por defecto del ciclo de vida No es un plugin por defecto, con lo que para poder usarlo es necesario decláralo en el pom.xml (junto a los objetivos que cubre) Los tests se ejecutan y se validan en la fase test: mvn test Los tests se ejecutan en la fase integration-test y se validan en la fase verify: mvn verify Conceptualmente se usa para tests unitarios Conceptualmente se usa para tests de integración Por defecto los tests se localizan siguiendo un patrón en los nombres: • **/Test*.java • **/*Test.java • **/*Tests.java • **/*TestCase.java Por defecto los tests se localizan siguiendo un patrón en los nombres: • **/IT*.java • **/*IT.java • **/*ITCase.java La versión por defecto de Surefire está definida en un fichero interno de Maven Por defecto se usa la última versión disponible de Failsafe Como vamos a ver, en JUnit 5 podemos usar ambos plugins
  • 28. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Ejecutar JUnit 5 desde Maven (versiones 5.0.x and 5.1.x): <properties> <junit.jupiter.version>5.0.3</junit.jupiter.version> <junit.platform.version>1.0.3</junit.platform.version> <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version> </properties> <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit.jupiter.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <dependencies> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit.platform.version}</version> </dependency> </dependencies> </plugin> </plugins> </build> Todos los ejemplos están disponibles en GitHub https://github.com/bonigarcia/ mastering-junit5 <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit.jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit.jupiter.version}</version> <scope>runtime</scope> </dependency> </dependencies> Para ser precisos, se necesita la API en tiempo de compilación y el engine en tiempo de ejecución
  • 29. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Ejecutar JUnit 5 desde Maven (versiones 5.2+): <properties> <junit.jupiter.version>5.2.0</junit.jupiter.version> <maven-surefire-plugin.version>2.22.0</maven-surefire-plugin.version> </properties> <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit.jupiter.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> </plugin> </plugins> </build> La versión 2.22.0 de Surefire (y Failsafe) tienen soporte nativo de JUnit 5
  • 30. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Ejecutar JUnit 5 desde Maven (versiones 5.2+): <properties> <junit.jupiter.version>5.2.0</junit.jupiter.version> <maven-surefire-plugin.version>2.22.0</maven-surefire-plugin.version> </properties> <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit.jupiter.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> </execution> </executions> </plugin> </plugins> </build> Si además de Surefire queremos usar Failsafe, hay que declararlo
  • 31. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Ejecutar JUnit 5 desde Gradle (versiones inferiores de Gradle 4.8): buildscript { ext { junitPlatformVersion = '1.0.0' } repositories { mavenCentral() } dependencies { classpath("org.junit.platform:junit-platform-gradle-plugin:${junitPlatformVersion}") } } repositories { mavenCentral() } ext { junitJupiterVersion = '5.0.0' } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.junit.platform.gradle.plugin' compileTestJava { sourceCompatibility = 1.8 targetCompatibility = 1.8 options.compilerArgs += '-parameters' } dependencies { testCompile("org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}") testRuntime("org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}") }
  • 32. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Ejecutar JUnit 5 desde Gradle (a partir de Gradle 4.8): repositories { mavenCentral() } ext { junitJupiterVersion = '5.2.0' } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' test { useJUnitPlatform() testLogging { events "passed", "skipped", "failed" } } compileTestJava { sourceCompatibility = 1.8 targetCompatibility = 1.8 options.compilerArgs += '-parameters' } dependencies { testCompile("org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}") } Gradle 4.8 tiene soporte nativo de JUnit 5
  • 33. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Ejecutar JUnit 5 desde IDEs como IntelliJ y Eclipse:
  • 34. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • De forma conceptual, una aserción (o predicado) está formada por • Datos esperados, obtenidos de lo que se conoce como oráculo (típicamente la especificación del SUT) • Datos reales, obtenidos de ejercitar el sistema bajo pruebas (SUT) • Un operador lógico que compara ambos valores
  • 35. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Podemos ver el ciclo de vida un caso de prueba en JUnit 5 (con aserciones) de la siguiente manera:
  • 36. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Las aserciones básicas en Jupiter son las siguientes: Aserción Descripción fail Hace fallar un test proporcionando un mensaje de error u excepción assertTrue Evalúa si una condición es cierta assertFalse Evalúa si una condición es false assertNull Evalúa si un objeto es null assertNotNull Evalúa si un objeto no es null assertEquals Evalúa si un objeto es igual a otro assertNotEquals Evalúa si un objeto no es igual a otro assertArrayEquals Evalúa si un array es igual a otros assertIterableEquals Evalúa si dos objetos iterables son iguales assertLinesMatch Evlúa si dos listas de String son iguales assertSame Evalúa si un objeto es el mismo que otro assertNotSame Evalúa si un objeto no es el mismo que otro Métodos estáticos de la clase Assertions
  • 37. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Un grupo de aserciones se evalúa mediante assertAll: import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; class GroupedAssertionsTest { @Test void groupedAssertions() { Address address = new Address("John", "Smith"); assertAll("address", () -> assertEquals("John", address.getFirstName()), () -> assertEquals("User", address.getLastName())); } } ------------------------------------------------------- T E S T S ------------------------------------------------------- Running io.github.bonigarcia.GroupedAssertionsTest Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.124 sec <<< FAILURE! - in io.github.bonigarcia.GroupedAssertionsTest groupedAssertions() Time elapsed: 0.08 sec <<< FAILURE! org.opentest4j.MultipleFailuresError: address (1 failure) expected: <User> but was: <Smith> at io.github.bonigarcia.GroupedAssertionsTest.groupedAssertio ns(GroupedAssertionsTest.java:32) Results : Failed tests: GroupedAssertionsTest.groupedAssertions:32 address (1 failure) expected: <User> but was: <Smith> Tests run: 1, Failures: 1, Errors: 0, Skipped: 0 En este ejemplo la segunda aserción no se cumple
  • 38. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Las ocurrencia de excepciones se implementa mediante la aserción assertThrows: import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; class ExceptionTest { @Test void exceptionTesting() { Throwable exception = assertThrows(IllegalArgumentException.class, () -> { throw new IllegalArgumentException("a message"); }); assertEquals("a message", exception.getMessage()); } } En este ejemplo el test pasará ya que estamos esperando la excepción IllegalArgumentException, y de decho ocurre dentro una expression lambda
  • 39. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Para evaluar timeouts podemos usar la aserción assertTimeout: import static java.time.Duration.ofMillis; import static java.time.Duration.ofMinutes; import static org.junit.jupiter.api.Assertions.assertTimeout; import org.junit.jupiter.api.Test; class TimeoutExceededTest { @Test void timeoutNotExceeded() { assertTimeout(ofMinutes(2), () -> { // Perform task that takes less than 2 minutes }); } @Test void timeoutExceeded() { assertTimeout(ofMillis(10), () -> { // Simulate task that takes more than 10 ms Thread.sleep(100); }); } } ------------------------------------------------------- T E S T S ------------------------------------------------------- Running io.github.bonigarcia.TimeoutExceededTest Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.18 sec <<< FAILURE! - in io.github.bonigarcia.TimeoutExceededTest timeoutExceeded() Time elapsed: 0.126 sec <<< FAILURE! org.opentest4j.AssertionFailedError: execution exceeded timeout of 10 ms by 90 ms at io.github.bonigarcia.TimeoutExceededTest.timeoutExceeded( TimeoutExceededTest.java:36) Results : Failed tests: TimeoutExceededTest.timeoutExceeded:36 execution exceeded timeout of 10 ms by 90 ms Tests run: 2, Failures: 1, Errors: 0, Skipped: 0 Este test pasará Este test fallará
  • 40. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Si queremos aserciones todavía más avanzadas, se recomienda usar librerías específicas, como por ejemplo Hamcrest: import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.jupiter.api.Test; class HamcrestTest { @Test void assertWithHamcrestMatcher() { assertThat(2 + 1, equalTo(3)); assertThat("Foo", notNullValue()); assertThat("Hello world", containsString("world")); } } ------------------------------------------------------- T E S T S ------------------------------------------------------- Running io.github.bonigarcia.HamcrestTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.059 sec - in io.github.bonigarcia.HamcrestTest Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 http://hamcrest.org/
  • 41. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Podemos declarar nombres personalizados a los métodos de prueba mediante la anotación @DisplayName: import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @DisplayName("A special test case") class DisplayNameTest { @Test @DisplayName("Custom test name containing spaces") void testWithDisplayNameContainingSpaces() { } @Test @DisplayName("╯°□°)╯") void testWithDisplayNameContainingSpecialCharacters() { } @Test @DisplayName("😱") void testWithDisplayNameContainingEmoji() { } } IntelliJ IDEA: 2016.2+
  • 42. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Las clases y métodos de test en JUnit 5 se pueden etiquetar usando la anotación @Tag import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @Tag("functional") class FunctionalTest { @Test void test1() { System.out.println("Functional Test 1"); } @Test void test2() { System.out.println("Functional Test 2"); } } import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @Tag("non-functional") class NonFunctionalTest { @Test @Tag("performance") @Tag("load") void test1() { System.out.println("Non-Functional Test 1 (Performance/Load)"); } @Test @Tag("performance") @Tag("stress") void test2() { System.out.println("Non-Functional Test 2 (Performance/Stress)"); } @Test @Tag("security") void test3() { System.out.println("Non-Functional Test 3 (Security)"); } @Test @Tag("usability") void test4() { System.out.println("Non-Functional Test 4 (Usability)"); } } • Estas etiquetas se pueden usar después para el descubrimiento y ejecución de los test (filtrado)
  • 43. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Para filtrar por tags en Maven: <properties> <junit-jupiter.version>5.3.0-M1</junit-jupiter.version> <junit-platform.version>1.3.0-M1</junit-platform.version> <maven-surefire-plugin.version>2.22.0</maven-surefire-plugin.version> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <dependencies> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit-platform.version}</version> </dependency> </dependencies> <configuration> <properties> <includeTags>functional</includeTags> <excludeTags>non-functional</excludeTags> </properties> </configuration> </plugin> </plugins> </build> ------------------------------------------------------- T E S T S ------------------------------------------------------- Running io.github.bonigarcia.FunctionalTest Functional Test 2 Functional Test 1 Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.075 sec - in io.github.bonigarcia.FunctionalTest Running io.github.bonigarcia.NonFunctionalTest Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 sec - in io.github.bonigarcia.NonFunctionalTest Results : Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
  • 44. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Para filtrar por tags en Gradle: test { useJUnitPlatform { includeTags 'non-functional' excludeTags 'functional' includeEngines 'junit-jupiter' excludeEngines 'junit-vintage' } filter { includeTestsMatching '.*Spec' includeTestsMatching '.*Test' includeTestsMatching '.*Tests' includeTestsMatching 'io.github.bonigarcia.*' } testLogging { events "passed", "skipped", "failed" } } mastering-junit5junit5-tagging-filtering>gradle test --rerun-tasks > Task :junit5-tagging-filtering:test io.github.bonigarcia.NonFunctionalTest > testOne() PASSED io.github.bonigarcia.NonFunctionalTest > testTwo() PASSED io.github.bonigarcia.NonFunctionalTest > testThree() PASSED io.github.bonigarcia.NonFunctionalTest > testFour() PASSED BUILD SUCCESSFUL in 2s 2 actionable tasks: 2 executed
  • 45. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • La anotación @Disabled se usa para deshabilitar tests. Se puede usar tanto a nivel de clase como a nivel de método import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; class DisabledTest { @Disabled @Test void skippedTest() { } } ------------------------------------------------------- T E S T S ------------------------------------------------------- Running io.github.bonigarcia.DisabledTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 0.03 sec - in io.github.bonigarcia.DisabledTest Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 1 import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @Disabled("All test in this class will be skipped") class AllDisabledTest { @Test void skippedTest1() { } @Test void skippedTest2() { } } ------------------------------------------------------- T E S T S ------------------------------------------------------- Running io.github.bonigarcia.AllDisabledTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 0.059 sec - in io.github.bonigarcia.AllDisabledTest Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 1
  • 46. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Desde JUnit 5.1 existe la anotación @DisabledOnOs para deshabilitar tests en función del sistema operativo import static org.junit.jupiter.api.condition.OS.LINUX; import static org.junit.jupiter.api.condition.OS.MAC; import static org.junit.jupiter.api.condition.OS.WINDOWS; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; class BuiltinDisabledOnOsTest { @DisabledOnOs(LINUX) @Test void notLinuxTest() { System.out.println("Disabled on Linux"); } @DisabledOnOs(WINDOWS) @Test void notWinTest() { System.out.println("Disabled on Windows"); } @DisabledOnOs(MAC) @Test void notMacTest() { System.out.println("Disabled on Mac"); } }
  • 47. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Las asunciones sirven para ignorar un test (o una parte del mismo) en base a una condición • Hay tres asunciones en JUnit 5: assumeTrue, assumeFalse, y assumingThat import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.Assumptions.assumingThat; import org.junit.jupiter.api.Test; class AssumptionsTest { @Test void assumeTrueTest() { assumeTrue(false); fail("Test 1 failed"); } @Test void assumeFalseTest() { assumeFalse(this::getTrue); fail("Test 2 failed"); } private boolean getTrue() { return true; } @Test void assummingThatTest() { assumingThat(false, () -> fail("Test 3 failed")); } } ------------------------------------------------------- T E S T S ------------------------------------------------------- org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines INFORMACIÓN: Discovered TestEngines with IDs: [junit-jupiter] Running io.github.bonigarcia.AssumptionsTest Tests run: 3, Failures: 0, Errors: 0, Skipped: 2, Time elapsed: 0.093 sec - in io.github.bonigarcia.AssumptionsTest Results : Tests run: 3, Failures: 0, Errors: 0, Skipped: 2 La asunción assumingThat se usa para condicionar la ejecución de una parte del test
  • 48. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • La anotación @RepeatedTest permite repetir la ejecución de un test un número determinado de veces • Cada repetición se comportará exactamente igual que un test normal, con su correspondiente ciclo de vida import org.junit.jupiter.api.RepeatedTest; class SimpleRepeatedTest { @RepeatedTest(5) void test() { System.out.println("Repeated test"); } } ------------------------------------------------------- T E S T S ------------------------------------------------------- Running io.github.bonigarcia.SimpleRepeatedTest Repeated test Repeated test Repeated test Repeated test Repeated test Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.11 sec - in io.github.bonigarcia.SimpleRepeatedTest Results : Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
  • 49. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Los test parametrizados reutilizan la misma lógica con diferentes datos de prueba • Para implementar este tipo de test, lo primero es añadir el módulo junit-jupiter-params en nuestro proyecto <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <version>${junit.jupiter.version}</version> <scope>test</scope> </dependency> </dependencies> dependencies { testCompile("org.junit.jupiter:junit-jupiter-params:${junitJupiterVersion}") }
  • 50. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • Los pasos para implementar un test parametrizado son: 1. Usar la anotación @ParameterizedTest para declarar un test como parametrizado 2. Elegir un proveedor de argumentos (argument provider) Arguments provider Descripción @ValueSource Usado para especificar un array de valores String, int, long, o double @EnumSource Usado para especificar valores enumerados (java.lang.Enum) @MethodSource Usado para especificar un método estático de la clase que proporciona un Stream de valores @CsvSource Usado para especificar valores separados por coma, esto es, en formato CSV (comma-separated values) @CsvFileSource Usado para especificar valores en formato CSV en un fichero localizado en el classpath @ArgumentsSource Usado para especificar una clase que implementa el interfaz org.junit.jupiter.params.provider.ArgumentsProvider
  • 51. 3. Jupiter: el nuevo modelo de programación de JUnit 5 import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; class ValueSourcePrimitiveTypesParameterizedTest { @ParameterizedTest @ValueSource(ints = { 0, 1 }) void testWithInts(int argument) { System.out .println("Parameterized test with (int) argument: " + argument); assertNotNull(argument); } @ParameterizedTest @ValueSource(longs = { 2L, 3L }) void testWithLongs(long argument) { System.out.println( "Parameterized test with (long) argument: " + argument); assertNotNull(argument); } @ParameterizedTest @ValueSource(doubles = { 4d, 5d }) void testWithDoubles(double argument) { System.out.println( "Parameterized test with (double) argument: " + argument); assertNotNull(argument); } } @ValueSource
  • 52. 3. Jupiter: el nuevo modelo de programación de JUnit 5 import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.concurrent.TimeUnit; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; class EnumSourceParameterizedTest { @ParameterizedTest @EnumSource(TimeUnit.class) void testWithEnum(TimeUnit argument) { System.out.println( "Parameterized test with (TimeUnit) argument: " + argument); assertNotNull(argument); } } @EnumSource
  • 53. 3. Jupiter: el nuevo modelo de programación de JUnit 5 import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; class MethodSourceStringsParameterizedTest { static Stream<String> stringProvider() { return Stream.of("hello", "world"); } @ParameterizedTest @MethodSource("stringProvider") void testWithStringProvider(String argument) { System.out.println( "Parameterized test with (String) argument: " + argument); assertNotNull(argument); } } @MethodSource
  • 54. 3. Jupiter: el nuevo modelo de programación de JUnit 5 import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; class CsvSourceParameterizedTest { @ParameterizedTest @CsvSource({ "hello, 1", "world, 2", "'happy, testing', 3" }) void testWithCsvSource(String first, int second) { System.out.println("Parameterized test with (String) " + first + " and (int) " + second); assertNotNull(first); assertNotEquals(0, second); } } @CsvSource
  • 55. 3. Jupiter: el nuevo modelo de programación de JUnit 5 import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvFileSource; class CsvFileSourceParameterizedTest { @ParameterizedTest @CsvFileSource(resources = "/input.csv") void testWithCsvFileSource(String first, int second) { System.out.println("Yet another parameterized test with (String) " + first + " and (int) " + second); assertNotNull(first); assertNotEquals(0, second); } } @CsvFileSource
  • 56. 3. Jupiter: el nuevo modelo de programación de JUnit 5 import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; class ArgumentSourceParameterizedTest { @ParameterizedTest @ArgumentsSource(CustomArgumentsProvider1.class) void testWithArgumentsSource(String first, int second) { System.out.println("Parameterized test with (String) " + first + " and (int) " + second); assertNotNull(first); assertTrue(second > 0); } } @ArgumentsSource import java.util.stream.Stream; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; public class CustomArgumentsProvider1 implements ArgumentsProvider { @Override public Stream<? extends Arguments> provideArguments( ExtensionContext context) { System.out.println("Arguments provider [1] to test " + context.getTestMethod().get().getName()); return Stream.of(Arguments.of("hello", 1), Arguments.of("world", 2)); } }
  • 57. 3. Jupiter: el nuevo modelo de programación de JUnit 5 • A medio cambio entre los test parametrizados y el modelo de extensión nos encontramos las plantillas de tests • Los métodos anotados con @TestTemplate se ejecutarán múltiples veces dependiendo de los valores devueltos por el proveedor de contexto • Las plantillas de test se usan siempre en conjunción con el punto de extensión TestTemplateInvocationContextProvider import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(MyTestTemplateInvocationContextProvider.class) class TemplateTest { @TestTemplate void testTemplate(String parameter) { System.out.println(parameter); } } En este caso deberíamos implementar la extensión MyTestTemplateInvocationContextProvider
  • 58. Contenidos 1. Introducción a las pruebas en el software 2. Motivación y arquitectura de JUnit 5 3. Jupiter: el nuevo modelo de programación de JUnit 5 4. Modelo de extensiones en JUnit 5 5. Selenium-Jupiter 6. Conclusiones
  • 59. 4. Modelo de extensiones en JUnit 5 • El modelo de extensión de JUnit 5 (también llamado Jupiter) permite ampliar el modelo de programación con funcionalidades personalizadas • Gracias al modelo de extensiones, frameworks externos pueden proporcionar integración con JUnit 5 de una manera sencilla • Hay 3 formas de usar una extensión en Jupiter: 1. Declarativamente, usando la anotación @ExtendWith (se puede usar a nivel de clase o de método) 2. Programáticamente, usando la anotación @RegisterExtension (la diferencia es que tenemos una instancia de la extensión disponible en la clase de tests) 3. Automáticamente, usando el mecanismo de carga de servicios de Java a través de la clase java.util.ServiceLoader
  • 60. 4. Modelo de extensiones en JUnit 5 1. Declarativamente (@ExtendWith): import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(MyExtension.class) public class MyTest { @Test public void test1() { // ... } @Test public void test2() { // ... } } import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; public class MyTest { @ExtendWith(MyExtension.class) @Test public void test1() { // ... } @Test public void test2() { // ... } } Declarada a nivel de clase, la extensión estará registrada para todos los tests de la clase Declarada a nivel de test, la extensión estará registrada únicamente para dicho test
  • 61. 4. Modelo de extensiones en JUnit 5 2. Programáticamente (@RegisterExtension) import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public class MyTest { @RegisterExtension MyExtension myExtension = new MyExtension(); @Test public void test1() { // ... } @Test public void test2() { // ... } } import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public class MyTest { @RegisterExtension static MyExtension myExtension = new MyExtension(); @Test public void test1() { // ... } @Test public void test2() { // ... } } La instancia de la extensión registrada mediante @RegisterExtension puede ser usada programáticamente para configuración de la extensión o en los propios tests No es obligatorio declarar la instancia como estático, aunque de esta forma se limitan los puntos de extensión a nivel de instancia (BeforeEachCallback, AfterEachCallback, etc.)
  • 62. 4. Modelo de extensiones en JUnit 5 3. Automáticamente (java.util.ServiceLoader) • Para registrar una extensión mediante este mecanismo, en primer lugar hay que declarar el nombre cualificado de la extensión en el siguiente fichero: /META-INF/services/org.junit.jupiter.api.extension.Extension • Después hay que pasar la siguiente propiedad de la JVM: -Djunit.jupiter.extensions.autodetection.enabled=true > mvn test -Djunit.jupiter.extensions.autodetection.enabled=true > gradle test -Djunit.jupiter.extensions.autodetection.enabled=true <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <configuration> <properties> <configurationParameters> junit.jupiter.extensions.autodetection.enabled=true </configurationParameters> </properties> </configuration> </plugin> test { useJUnitPlatform() testLogging { events "passed", "skipped", "failed" } testLogging.showStandardStreams = true systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true' }
  • 63. 4. Modelo de extensiones en JUnit 5 • Las extensiones en Jupiter se implementan haciendo uso de los llamados puntos de extensión • Los puntos de extensión son interfaces que permiten declarar cuatro tipos de operaciones: 1. Añadir nueva lógica dentro del ciclo de vida de tests 2. Realizar ejecución condicional de tests 3. Realizar inyección de dependencias en métodos de tests 4. Gestionar las plantillas de test
  • 64. 4. Modelo de extensiones en JUnit 5 • Los puntos de extensión que controlan el ciclo de vida de tests son los siguientes: Punto de extensión Implementadas por extensiones que se ejecutarán… TestInstancePostProcessor Justo después de la instanciación del test BeforeAllCallback Antes de todos los tests de una clase BeforeEachCallback Antes de cada test BeforeTestExecutionCallback Justo antes de cada test TestExecutionExceptionHandler Justo después de que ocurra una de excepciones en el test AfterTestExecutionCallback Justo después de cada test AfterEachCallback Después de cada test AfterAllCallback Después de todos los tests
  • 65. 4. Modelo de extensiones en JUnit 5 • El ciclo de vida completo teniendo en puntos de extensión y código de usuario (@BeforeAll, @BeforeEach, @AfterEach, @AfterAll) es:
  • 66. 4. Modelo de extensiones en JUnit 5 • Ejemplo: extensión que ignora las excepciones de tipo IOException package io.github.bonigarcia; import java.io.IOException; import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; import org.junit.jupiter.api.extension.TestExtensionContext; public class IgnoreIOExceptionExtension implements TestExecutionExceptionHandler { @Override public void handleTestExecutionException(TestExtensionContext context, Throwable throwable) throws Throwable { if (throwable instanceof IOException) { return; } throw throwable; } } package io.github.bonigarcia; import java.io.IOException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; public class ExceptionTest { @ExtendWith(IgnoreIOExceptionExtension.class) @Test public void test1() throws IOException { throw new IOException("My IO Exception"); } @Test public void test2() throws IOException { throw new IOException("My IO Exception"); } } En este ejemplo el primer caso de prueba pasará mientras el segundo fallará
  • 67. 4. Modelo de extensiones en JUnit 5 • La ejecución condicional de tests se realiza mediante el punto de extensión ExecutionCondition: package io.github.bonigarcia; import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ExecutionCondition; import org.junit.jupiter.api.extension.ExtensionContext; public class MyConditionalExtension implements ExecutionCondition { @Override public ConditionEvaluationResult evaluateExecutionCondition( ExtensionContext arg0) { boolean condition = ... if (condition) { return ConditionEvaluationResult.enabled("reason"); } else { return ConditionEvaluationResult.disabled("reason"); } } } package io.github.bonigarcia; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(MyConditionalExtension.class) class MyExtensionTest { @Test void test() { // my test } } En función de lo que devuelva la extensión, los tests se ejecutarán o serán ignorados
  • 68. 4. Modelo de extensiones en JUnit 5 • La inyección de dependencias se realiza mediante el punto de extensión ParameterResolver: package io.github.bonigarcia; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolver; public class MyParameterResolver implements ParameterResolver { @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { return true; } @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { return "my parameter"; } } package io.github.bonigarcia; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; public class MyTest { @ExtendWith(MyParameterResolver.class) @Test public void test(Object parameter) { System.out.println("---> parameter " + parameter); } } ------------------------------------------------------- T E S T S ------------------------------------------------------- mar 13, 2017 5:37:36 PM org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines INFORMACIÓN: Discovered TestEngines with IDs: [junit-jupiter] Running io.github.bonigarcia.MyTest ---> parameter my parameter Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.059 sec - in io.github.bonigarcia.MyTest Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
  • 69. 4. Modelo de extensiones en JUnit 5 • La gestión de plantillas de tests (@TestTemplate) se implementa mediante el punto de extensión TestTemplateInvocationContextProvider import java.util.stream.Stream; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.TestTemplateInvocationContext; import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; public class MyTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider { @Override public boolean supportsTestTemplate(ExtensionContext context) { return true; } @Override public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts( ExtensionContext context) { return Stream.of(invocationContext("test-1"), invocationContext("test-2")); } private TestTemplateInvocationContext invocationContext(String parameter) { return new TestTemplateInvocationContext() { @Override public String getDisplayName(int invocationIndex) { return parameter; } }; } } package io.github.bonigarcia; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; class TemplateTest { @TestTemplate @ExtendWith(MyTestTemplateInvocationContextProvider.class) void testTemplate() { } }
  • 70. 4. Modelo de extensiones en JUnit 5 • Extensión de Mockito para JUnit 5 (pruebas unitarias): import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) class LoginControllerLoginTest { // Mocking objects @InjectMocks LoginController loginController; @Mock LoginService loginService; // Test data UserForm userForm = new UserForm("foo", "bar"); @Test void testLoginOk() { // Setting expectations (stubbing methods) when(loginService.login(userForm)).thenReturn(true); // Exercise SUT String reseponseLogin = loginController.login(userForm); // Verification assertEquals("OK", reseponseLogin); verify(loginService).login(userForm); verifyNoMoreInteractions(loginService); } @Test void testLoginKo() { // Setting expectations (stubbing methods) when(loginService.login(userForm)).thenReturn(false); // Exercise SUT String reseponseLogin = loginController.login(userForm); // Verification assertEquals("KO", reseponseLogin); verify(loginService).login(userForm); verifyZeroInteractions(loginService); } }
  • 71. 4. Modelo de extensiones en JUnit 5 • Extensión de Spring para JUnit 5 (pruebas de integración): import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { MySpringApplication.class }) class SimpleSpringTest { @Autowired public MessageComponent messageComponent; @Test public void test() { assertEquals("Hello world!", messageComponent.getMessage()); } } spring framework
  • 72. 4. Modelo de extensiones en JUnit 5 • Extensión de Spring para JUnit 5 (pruebas de integración): spring boot import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest class SimpleSpringBootTest { @Autowired public MessageComponent messageComponent; @Test public void test() { assertEquals("Hello world!", messageComponent.getMessage()); } }
  • 73. 4. Modelo de extensiones en JUnit 5 • Extensión de Selenium para JUnit 5 (pruebas end-to-end): import static io.github.bonigarcia.BrowserType.CHROME; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.remote.RemoteWebDriver; import io.github.bonigarcia.DockerBrowser; import io.github.bonigarcia.SeleniumExtension; @ExtendWith(SeleniumExtension.class) public class LocalWebDriverTest { @Test public void testLocalChrome(ChromeDriver driver) { driver.get("https://bonigarcia.github.io/selenium-jupiter/"); assertTrue(chrome.getTitle().startsWith("Selenium-Jupiter")); } @Test public void testDockerChrome( @DockerBrowser(type = CHROME) RemoteWebDriver driver) { driver.get("http://www.seleniumhq.org/"); assertTrue(firefox.getTitle().startsWith("Selenium")); } } https://bonigarcia.github.io/selenium-jupiter/
  • 74. 4. Modelo de extensiones en JUnit 5 • Ejemplo completo: • Aplicación web implementada con Spring-Boot • Pruebas unitarias con Mockito • Pruebas de integración con Spring • Pruebas de sistema (e2e) con Selenium • Ejecución de pruebas mediante Travis CI • Análisis de código mediante SonarCloud • Análisis de cobertura mediante Codedov https://github.com/bonigarcia/rate-my-cat
  • 75. Contenidos 1. Introducción a las pruebas en el software 2. Motivación y arquitectura de JUnit 5 3. Jupiter: el nuevo modelo de programación de JUnit 5 4. Modelo de extensiones en JUnit 5 5. Selenium-Jupiter 6. Conclusiones
  • 76. 5. Selenium-Jupiter • Selenium-Jupiter permite usar Selenium (WebDriver y Grid) desde casos de prueba JUnit 5 de manera sencilla • De forma interna, Selenium-Jupiter implementa tres puntos de extensión de JUnit 5: public class SeleniumExtension implements ParameterResolver, AfterEachCallback, TestTemplateInvocationContextProvider { @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { // Logic for dependency injection } @Override public void afterEach(ExtensionContext context) { // Logic for clean resources after test } @Override public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts( ExtensionContext extensionContext) { // Logic for support test templates } }
  • 77. 5. Selenium-Jupiter • Selenium-Jupiter permite usar diferentes tipos de browser 1. Locales (instalados en la máquina que ejecuta el test, a través de Selenium WebDirver) 2. Remotos (instalados en otras máquinas, a través de Selenium Grid) 3. En contenedores Docker (a través de contendores Docker y Selenium Grid) • Para navegadores locales, la gestión de los binarios necesarios, Selenium-Jupiter usa WebDriverManager https://github.com/bonigarcia/webdrivermanager
  • 78. 5. Selenium-Jupiter • Ejemplo de uso de navegadores locales: import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.firefox.FirefoxDriver; import io.github.bonigarcia.SeleniumExtension; @ExtendWith(SeleniumExtension.class) public class ChromeAndFirefoxJupiterTest { @Test public void testWithOneChrome(ChromeDriver chromeDriver) { // Use Chrome in this test } @Test public void testWithFirefox(FirefoxDriver firefoxDriver) { // Use Firefox in this test } @Test public void testWithChromeAndFirefox(ChromeDriver chromeDriver, FirefoxDriver firefoxDriver) { // Use Chrome and Firefox in this test } } Selenium-Jupiter se encarga de instanciar e inyectar objetos de los siguientes tipos: • ChromeDriver • FirefoxDriver • EdgeDriver • OperaDriver • SafariDriver • HtmlUnitDriver • PhantomJSDriver • InternetExplorerDriver • AppiumDriver
  • 79. 5. Selenium-Jupiter • Ejemplo de uso de navegadores remotos: import static org.openqa.selenium.remote.DesiredCapabilities.firefox; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.openqa.selenium.Capabilities; import org.openqa.selenium.remote.RemoteWebDriver; import io.github.bonigarcia.DriverCapabilities; import io.github.bonigarcia.DriverUrl; import io.github.bonigarcia.SeleniumExtension; @ExtendWith(SeleniumExtension.class) public class RemoteWebDriverJupiterTest { @DriverUrl String url = "http://localhost:4444/wd/hub"; @DriverCapabilities Capabilities capabilities = firefox(); @Test void testWithRemoteChrome(@DriverUrl("http://localhost:4444/wd/hub") @DriverCapabilities("browserName=chrome") RemoteWebDriver driver) { // Use remote Chrome in this test } @Test void testWithRemoteFirefox(RemoteWebDriver driver) { // Use remote Firefox in this test } } Selenium-Jupiter proporciona las anotación @DriverUrl para definir la URL del Selenium/Appium Server y @DriverCapabilities para las capacidades requeridas
  • 80. 5. Selenium-Jupiter • Ejemplo de uso de navegadores en contenedores Docker: import static io.github.bonigarcia.BrowserType.CHROME; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.openqa.selenium.remote.RemoteWebDriver; import io.github.bonigarcia.DockerBrowser; import io.github.bonigarcia.SeleniumExtension; @ExtendWith(SeleniumExtension.class) public class DockerChromeJupiterTest { @Test public void testChrome( @DockerBrowser(type = CHROME) RemoteWebDriver driver) { driver.get("https://bonigarcia.github.io/selenium-jupiter/"); assertThat(driver.getTitle(), containsString("JUnit 5 extension for Selenium")); } @Test public void testChromeWithVersion(@DockerBrowser(type = CHROME, version = "67.0") RemoteWebDriver driver) { driver.get("https://bonigarcia.github.io/selenium-jupiter/"); assertThat(driver.getTitle(), containsString("JUnit 5 extension for Selenium")); } } Selenium-Jupiter proporciona las anotación @DockerBrowser para declarar el uso de browsers en contenedores Docker Si no se especifica el parámetro, version la última versión disponible en Docker Hub se usará Los tipos de browser soportados actualmente son: CHROME, FIREFOX, OPERA, y ANDROID El parámetro version admite además los siguiente valores especiales: latest, latest-*, beta, y unstable
  • 81. 5. Selenium-Jupiter • Ejemplo de uso de navegadores en contenedores Docker: import static io.github.bonigarcia.BrowserType.ANDROID; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.openqa.selenium.remote.RemoteWebDriver; import io.github.bonigarcia.DockerBrowser; import io.github.bonigarcia.SeleniumExtension; @ExtendWith(SeleniumExtension.class) public class DockerAndroidCustomJupiterTest { @Test public void testAndroid(@DockerBrowser(type = ANDROID, version = "5.0.1", deviceName = "Nexus S", browserName = "browser") RemoteWebDriver driver) { driver.get("https://bonigarcia.github.io/selenium-jupiter/"); assertThat(driver.getTitle(), containsString("JUnit 5 extension for Selenium")); } } En el caso de dispositivos Android, a demás de la versión, el tipo de dispositivo y el navegador se puede especificar Android version API level Browser name 5.0.1 21 browser 5.1.1 22 browser 6.0 23 chrome 7.0 24 chrome 7.1.1 25 chrome Type Device name Phone Samsung Galaxy S6 Phone Nexus 4 Phone Nexus 5 Phone Nexus One Phone Nexus S Tablet Nexus 7
  • 82. 5. Selenium-Jupiter • Al usar browsers en contendores Docker, es posible interactuar con la sesión mediante VNC así como grabar dichas sesiones
  • 83. 5. Selenium-Jupiter • Selenium-Jupiter hace uso del soporte de JUnit 5 de plantillas de test • Se puede definir el número y tipo de browser: 1. Mediante un fichero JSON import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; import org.openqa.selenium.WebDriver; import io.github.bonigarcia.SeleniumExtension; import io.github.bonigarcia.SeleniumJupiter; @ExtendWith(SeleniumExtension.class) public class TemplateTest { @TestTemplate void templateTest(WebDriver driver) { driver.get("https://bonigarcia.github.io/selenium-jupiter/"); assertThat(driver.getTitle(), containsString("JUnit 5 extension for Selenium")); } } { "browsers": [ [ { "type": "chrome-in-docker", "version": "latest" } ], [ { "type": "chrome-in-docker", "version": "latest-1" } ], [ { "type": "chrome-in-docker", "version": "beta" } ], [ { "type": "chrome-in-docker", "version": "unstable" } ] ] }
  • 84. 5. Selenium-Jupiter • Selenium-Jupiter hace uso del soporte de JUnit 5 de plantillas de test • Se puede definir el número y tipo de browser: 2. Programáticamente import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.RegisterExtension; import org.openqa.selenium.WebDriver; import io.github.bonigarcia.BrowserBuilder; import io.github.bonigarcia.BrowsersTemplate.Browser; import io.github.bonigarcia.SeleniumExtension; public class TemplateRegisterTest { @RegisterExtension static SeleniumExtension seleniumExtension = new SeleniumExtension(); @BeforeAll static void setup() { Browser chrome = BrowserBuilder.chrome().build(); Browser firefox = BrowserBuilder.firefox().build(); seleniumExtension.addBrowsers(chrome); seleniumExtension.addBrowsers(firefox); } @TestTemplate void templateTest(WebDriver driver) { // ... } }
  • 85. 5. Selenium-Jupiter • Selenium-Jupiter permite el uso de browser genéricos, especificados haciendo uso de parámetros de configuración import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.openqa.selenium.WebDriver; import io.github.bonigarcia.SeleniumExtension; @ExtendWith(SeleniumExtension.class) public class GenericTest { @Test void genericTest(WebDriver driver) { driver.get("https://bonigarcia.github.io/selenium-jupiter/"); assertThat(driver.getTitle(), containsString("JUnit 5 extension for Selenium")); } } El browser por defecto se especifica mediante la clave de configuración sel.jup.default.browser (chrome-in-docker por defecto) Se define una lista de browser alternativos mediante la clave de configuración sel.jup.default.browser.fallback La configuración de Selenium-Jupiter también puede hacerse programática a través del método SeleniumJupiter.config()
  • 86. 5. Selenium-Jupiter • Selenium-Jupiter se integra fácilmente con Jenkins • Permite la exportación de ficheros (grabaciones, screenshots) a través del plugin de adjuntos de Jenkins (Jenkins attachment plugin) • Dando valor surefire-reports a la clave de configuración sel.jup.output.folder, Selenium- Jupiter almacenará los ficheros generados en los tests en la ruta adecuada para que el plugin los encuentre y los publique en la GUI de Jenkins asociado a la ejecución del job
  • 87. Contenidos 1. Introducción a las pruebas en el software 2. Motivación y arquitectura de JUnit 5 3. Jupiter: el nuevo modelo de programación de JUnit 5 4. Modelo de extensiones en JUnit 5 5. Selenium-Jupiter 6. Conclusiones
  • 88. 6. Conclusiones • JUnit 5 ha supuesto el rediseño completo del framework JUnit • Es modular y está formado por Jupiter, Vintage, Platform • Ofrece tres tipos de APIs: Test API, Test Engine SPI, Test Launcher API • El modelo de programación en JUnit 5 (Jupiter) presenta muchas novedades con respecto a JUnit 4 • Jupiter puede crecer mediante un modelo de extensiones basado en diferentes puntos de extensión: ciclo de vida, ejecución condicional, inyección de dependencias, plantillas de test • La versión 5.3 de JUnit 5 está actualmente en desarrollo. En esta versión se centra en la ejecución paralela de tests, escenarios de test (organización de unidades de test), etc.
  • 89. 6. Conclusiones • Algunas features de JUnit 5 que no hemos visto en esta charla: • Test anidados (@Nested) • Test dinámicos (@TestFactory) • Soporte de reglas de JUnit 4 en JUnit 5 • Uso de interfaces y métodos por defecto para tests • Compatibilidad de JUnit 5 y Java 9 • Tampoco hemos visto la integración de JUnit 5 con: • Cucumber (hay un extensión propuesta y quieren hacer un test engine) • Docker (hay una extensión que permite ejecutar contenedores Dockers desde casos de prueba JUnit 5) • Android (plugin Gradle para ejecutar tests JUnit 5) • Servicios REST (con Spring, REST Assured, o WireMock) • Otros: integración continua, ciclo de vida del desarrollo, requisitos…
  • 90. Introducción y novedades de JUnit 5 Muchas gracias Boni García boni.garcia@urjc.es http://bonigarcia.github.io/ @boni_gg https://github.com/bonigarcia