JUnit 5
JUnit 5
=
JUnit Platform
+
JUnit Jupiter
+
JUnit Vintage
➔ JUnit Platform: É o responsável pela descoberta e execução de testes na
JVM, definindo a relação entre os testes e plataforma de execução (IDEs,
console, ferramentas de build). Esse módulo também expõe a interface
TestEngine, que define o contrato de execução de qualquer ferramenta de
testes sobre a plataforma do JUnit.
➔ JUnit Jupiter: Este módulo contém os novos recursos para construção de
testes usando o JUnit, e fornece uma implementação de TestEngine para
execução dos testes escritos com o JUnit Jupiter.
➔ JUnit Vintage: Fornece um TestEngine para execução de testes escritos em
JUnit 3 e 4.
JUnit Jupiter
import org.junit.jupiter.api.Test;
import static
org.junit.jupiter.api.Assertions.assertEquals;
public class Sample1 {
@Test
public void myFirstJUnit5Test() {
assertEquals(2, 1 + 1);
}
}
public class Sample2 {
@SlowTest
public void testWithMyAnnotation() {
assertEquals(2, verySlowComputation());
}
}
@Test
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SlowTest {
}
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
public class Sample3 {
@BeforeAll
public static void beforeAll() {
System.out.println("Hello, i'm running 'before all'");
}
@BeforeEach
public void beforeEach() {
System.out.println("Hello, i'm running 'before each'");
}
@Test
public void sample() {
System.out.println("Hello, i'm a test!");
}
}
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.AfterAll;
public class Sample3 {
@Test
public void sample() {
System.out.println("Hello, i'm a test!");
}
@AfterEach
public void afterEach() {
System.out.println("Hello, i'm running 'after each'");
}
@AfterAll
public static void afterAll() {
System.out.println("Hello, i'm running 'after all'");
}
}
import org.junit.jupiter.api.TestInstance;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
//@TestInstance(TestInstance.Lifecycle.PER_METHOD)
public class Sample4 {
@Test
public void sample() {
System.out.println("Hello, i'm a test!, on instance " +
this);
}
@Test
public void sample2() {
System.out.println("Hello, i'm a another test!, on instance "
+ this);
}
}
import org.junit.jupiter.api.DisplayName;
public class Sample5 {
@Test
@DisplayName("The sum (1 + 1) must be 2")
public void testWithDisplayName() {
assertEquals(2, 1 + 1);
}
@Test
@DisplayName("The sum (1 + 1) must be 2, with emoji 😃!")
public void testWithDisplayName2() {
assertEquals(2, 1 + 1);
}
}
@DisplayName - output
Assertions
import static org.junit.jupiter.api.Assertions.assertEquals;
public class Sample6 {
@Test
public void sample() {
assertEquals(2, 1 + 1, “The sum (1 + 1) must be 2”);
}
@Test
public void sample2() {
assertEquals(2, 1 + 1, () -> “The sum (1 + 1) must be
2”);
}
}
Assertions
assertAll
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
import static org.junit.jupiter.api.Assertions.assertAll;
public class Sample7 {
@Test
public void sample() {
Person person = new Person("Tiago de Freitas Lima", 20);
assertAll(
() -> assertEquals("Tiago Lima", person.name),
() -> assertEquals(32, person.age));
}
@Test
public void sample2() {
Person person = new Person("Tiago de Freitas Lima", 20);
assertAll("Something is wrong...",
() -> assertEquals("Tiago Lima", person.name),
() -> assertEquals(32, person.age));
}
}
Expected :Tiago Lima
Actual :Tiago de Freitas Lima
Expected :32
Actual :20
org.opentest4j.MultipleFailuresError:
Multiple Failures (2 failures)
expected: <Tiago Lima> but was: <Tiago de
Freitas Lima>
expected: <32> but was: <20>
Assertions
exceptions
class MyObject {
void dangerous(String arg) {
throw new
IllegalArgumentException("Invalid argument:
" + arg);
}
void safe() {
}
}
import static org.junit.jupiter.api.Assertions.assertThrows;
public class Sample8 {
@Test
public void sample() {
MyObject myObject = new MyObject();
assertThrows(IllegalArgumentException.class, () ->
myObject.dangerous("bla"));
}
@Test
public void sample2() {
MyObject myObject = new MyObject();
assertThrows(IllegalStateException.class, () ->
myObject.dangerous("bla"));
}
}
import static org.junit.jupiter.api.Assertions.assertThrows;
public class Sample8 {
@Test
public void sample3() {
MyObject myObject = new MyObject();
assertThrows(IllegalStateException.class, myObject::safe);
}
@Test
public void sample4() {
MyObject myObject = new MyObject();
IllegalArgumentException exception =
assertThrows(IllegalArgumentException.class,
() -> myObject.dangerous("bla"));
assertEquals("Invalid argument: bla", exception.getMessage());
}
}
Assertions
timeouts
class MyObject {
String slow() {
//...very slow…
return “”;
}
}
import static org.junit.jupiter.api.Assertions.assertTimeout;
public class Sample9 {
@Test
public void sample() {
MyObject myObject = new MyObject();
assertTimeout(Duration.ofMillis(2000), myObject::slow);
}
@Test
public void sample2() {
MyObject myObject = new MyObject();
String output = assertTimeout(Duration.ofMillis(2000), myObject::slow);
assertEquals("slow...", output);
}
}
import static
org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
public class Sample9 {
@Test
public void sample3() {
MyObject myObject = new MyObject();
assertTimeoutPreemptively(Duration.ofMillis(2000),
myObject::slow);
}
}
Parameterized Tests
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.0.0</version>
<scope>test</scope>
</dependency>
import org.junit.jupiter.params.ParameterizedTest;
import
org.junit.jupiter.params.provider.ValueSource;
public class Sample10 {
@ParameterizedTest
@ValueSource(strings = { "Hello", "World" })
public void parameterizedTest(String argument) {
assertNotNull(argument);
}
}
import
org.junit.jupiter.params.ParameterizedTest;
import
org.junit.jupiter.params.provider.EnumSource;
public class Sample11 {
@ParameterizedTest
@EnumSource(TimeUnit.class)
public void parameterizedTest(TimeUnit
timeUnit) {
assertNotNull(timeUnit);
}
}
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
public class Sample12 {
@ParameterizedTest
@MethodSource("parameterFactory")
public void parameterizedTest(String argument) {
assertNotNull(argument);
}
static Collection<String> parameterFactory() {
return Arrays.asList("Hello", "World");
}
}
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
public class Sample13 {
@ParameterizedTest
@MethodSource("parameterFactory")
public void parameterizedTest(String argument, int count) {
assertNotNull(argument);
assertTrue(count > 0);
}
static Collection<Arguments> parameterFactory() {
return Arrays.asList(Arguments.of("Hello", 1),
Arguments.of("World", 1));
}
}
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
public class Sample14 {
@ParameterizedTest
@CsvSource({ "Hello, 1", "World, 2"})
public void parameterizedTest(String argument,
int count) {
assertNotNull(argument);
assertTrue(count > 0);
}
}
import org.junit.jupiter.params.ParameterizedTest;
import
org.junit.jupiter.params.provider.CsvFileSource;
public class Sample15 {
@ParameterizedTest
@CsvFileSource(resources = "/parameters.csv")
public void parameterizedTest(String argument,
int count) {
assertNotNull(argument);
assertTrue(count > 0);
}
}
Parameterized Tests
custom argument
provider
import org.junit.jupiter.params.ParameterizedTest;
import
org.junit.jupiter.params.provider.ArgumentsSource;
public class Sample16 {
@ParameterizedTest
@ArgumentsSource(CustomArgumentProvider.class)
public void parameterizedTest(String argument, int
count) {
assertNotNull(argument);
assertTrue(count > 0);
}
}
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
class CustomArgumentProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments>
provideArguments(ExtensionContext context) throws Exception {
return Stream.of(Arguments.of("Hello", 1),
Arguments.of("World", 1));
}
}
import org.junit.jupiter.params.ParameterizedTest;
public class Sample17 {
@ParameterizedTest
@ExcelSource(file = "test.xls")
public void parameterizedTest(String argument) {
assertNotNull(argument);
}
}
import org.junit.jupiter.params.provider.ArgumentsSource;
@ArgumentsSource(ExcelArgumentProvider.class)
@interface ExcelSource {
String file();
}
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.support.AnnotationConsumer;
class ExcelArgumentProvider implements ArgumentsProvider,
AnnotationConsumer<ExcelSource> {
private String fileName;
@Override
public void accept(ExcelSource excelSource) {
this.fileName = excelSource.file();
}
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext
context) throws Exception {
return Stream.empty();
}
}
Nested Tests
public class Sample18 {
private Stack<String> stack;
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
}
@Nested
@DisplayName("when new")
class WhenNew {
@BeforeEach
void createNewStack() {
stack = new Stack<>();
}
...
@Test
@DisplayName("is empty")
void isEmpty() {
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped() {
assertThrows(EmptyStackException.class, () -> stack.pop());
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing {
String anElement = "an element";
@BeforeEach
void pushAnElement() {
stack.push(anElement);
}
...
...
@Test
@DisplayName("it is no longer empty")
void isNotEmpty() {
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
assertEquals(anElement, stack.pop());
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked() {
assertEquals(anElement, stack.peek());
assertFalse(stack.isEmpty());
}
}
}
}
Nested tests - output
Test interfaces
public interface Testable<T> {
T createValue();
}
public interface EqualsContract<T> extends Testable<T> {
T createNotEqualValue();
@Test
default void valueEqualsItself() {
T value = createValue();
assertEquals(value, value);
}
@Test
default void valueDoesNotEqualNull() {
T value = createValue();
assertFalse(value.equals(null));
}
@Test
default void valueDoesNotEqualDifferentValue() {
T value = createValue();
T differentValue = createNotEqualValue();
assertNotEquals(value, differentValue);
assertNotEquals(differentValue, value);
}
}
public class Sample20 implements EqualsContract<String> {
@Override
public String createValue() {
return "Hello";
}
@Override
public String createNotEqualValue() {
return "World";
}
}
Tags
import org.junit.jupiter.api.Tag;
public class Sample21 {
@Test
@Tag("slow")
@Tag("whatever")
public void sample() {
assertNotNull("...");
}
}
O que mais?
https://engenharia.elo7.com.br/novidades-do-junit-5-parte-1/
https://engenharia.elo7.com.br/novidades-do-junit-5-parte-2/
Obrigado!
github.com/ljtfreitas/junit-5-samples

Junit 5 - Maior e melhor

Editor's Notes

  • #2 Apresentação geral do Junit5
  • #3 JUnit dividido em projetos com conceitos diferentes: Platform (descoberta e execução de testes), Jupiter (construção de testes), Vintage (compatibilidade com versões anteriores para execução no JUnit Platform)
  • #4 Explicação geral dos três níveis da arquitetura
  • #5 JUnit Jupiter = construção de testes. Exemplos a seguir.
  • #6 Exemplo básico - destacar: o pacote org.junit.jupiter não confilta com as versões anteriores. Classe Assert substituída por Assertions
  • #7 Uso de meta anotações (anotações marcadas com Test)
  • #8 BeforeAll - antiga BeforeClass BeforeEach - antiga Before
  • #9 AfterAll - antiga AfterClass AfterEach - antiga After
  • #10 Comentar sobre isolamento - cada teste em uma instância da classe. Possibilidade de alterar esse comportamento usando @TestInstance
  • #11 @DisplayName - saída no output
  • #12 Output no Intelij
  • #13 JUnit Jupiter = construção de testes. Exemplos a seguir.
  • #14 Comentar sobre o último parâmetro - mensagem exibida em caso de erro. No segundo exemplo a mensagem é fornecida por um Supplier Comentar que outros métodos continuam existindo (assertNotEquals, assertSame, assertTrue, etc)
  • #17 assertAll - encapsula vários asserts em um único teste
  • #18 Mensagem de erro do assertAll
  • #19 Testes de exceptions - comentar sobre como são feitos no Junit 4 (parametro expected da anotação Test, rule ExpectedException ou try/catch)
  • #21 Exemplo básico - destacar: o pacote org.junit.jupiter não confilta com as versões anteriores. Classe Assert substituída por Assertions
  • #22 assertThrows O segundo exemplo captura a exception lançada para validação
  • #23 Testes de timeouts - comentar sobre como são feitos no Junit 4 (parametro timeout da anotação Test, rule Timeout)
  • #24 Exemplo básico - destacar: o pacote org.junit.jupiter não confilta com as versões anteriores. Classe Assert substituída por Assertions
  • #25 assertTimeout. Executado na mesma thread O segundo exemplo captura o retorno do método para validação. O parametro é um ThrowingSupplier
  • #26 assertTimeoutPreemptively. Executado em outra thread
  • #27 Testes parametrizados. Explicar a motivação e como são feitos no Junit 4 (array bidimensional com parametros injetados via construtor)
  • #28 artefato necessário
  • #29 @ParameterizedTest ao inves de @Test @ValueSource - parameter provider do teste. Parametros fixos
  • #30 @EnumSource - teste executado para cada elemento do enum
  • #31 @MethodSource - método fornecedor de parametros. O retorno deve ser uma Collection, array ou Stream. Esse exemplo utiliza apenas um parâmetro por teste.
  • #32 @MethodSource - Esse exemplo utiliza multiplos parâmetro por teste, retornando uma coleção de Arguments
  • #33 @CvsSource - parâmetros no formato CSV (separados por vírgula)
  • #34 @CvsFileSource - permite informar um arquivo csv
  • #35 Argument providers customizados
  • #36 @ArgumentSource usando um CustomArgumentProvider
  • #37 CustomArgumentProvider - implements ArgumentsProvider
  • #38 custom annotation
  • #39 anotação deve estar anotada com @ArgumentsSource
  • #40 Também implementa AnnotationConsumer, que permite acesso a meta anotacao que encapsula o ArgumentProvider
  • #41 Testes aninhados - relação de dependencia e hierarquia
  • #42 Exemplo retirado da documentação - testes da classe Stack em varios cenarios
  • #45 Output no Intelij
  • #46 Testes utilizando default methods de interfaces
  • #47 Exemplo retirado da documentação
  • #48 Exemplo retirado da documentação
  • #49 Implementação da interface no teste - template method
  • #50 Tags em testes - utilizados para filtros