Writing Tests With JUnit 5 and CDI 2.0
This post provides a guide to JUnit's most recent release and its new features, with sample code for the updated assertions and extensions.
Join the DZone community and get the full member experience.
Join For FreeJUnit is the most popular test framework in the Java world. In the newest release, version 5.0.3, it brings new features that will make Java developers' lives more comfortable. It has Java 8 as a minimum requirement that allows further support for Java 8. This article will cover a simple sample with JUnit integrated with CDI 2.0.
In this version, there is break compatibility, such as the package org.junit.jupiter.api instead of org.junit:
JUnit 4 | Junit 5 |
Assert |
Assertions |
Assume |
Assumptions |
@Before |
@BeforeEach |
@After |
@AfterEach |
@BeforeClass |
@BeforeAll |
@AfterClass |
@AfterAll |
@Ignore |
@Disable |
@Category |
@Tag |
@RunWith, @Rule and @ClassRule |
@ExtendeWith |
To set up JUnit 5.0 in a Maven project:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.soujava</groupId>
<artifactId>jnunit</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>JUnit 5 set dependency</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.0.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
With the dependency defined, your project is ready to use JUnit 5:
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("Hello world test")
public class HelloWorldTest {
@BeforeAll
public static void beforeAll() {
System.out.println("Before all");
}
@AfterAll
public static void afterAll() {
System.out.println("After all");
}
@BeforeEach
public void beforeEach() {
System.out.println("Before each test");
}
@AfterEach
public void afterEach() {
System.out.println("Before after test");
}
@Test
@DisplayName("should returns equals values")
public void shouldEquals() {
Assertions.assertEquals(5, 2 + 3);
}
@Test
@DisplayName("should returns not equals values")
public void shouldNotEquals() {
Assertions.assertNotEquals("Brazil", "Argetina");
}
@Test
@DisplayName("should be not null")
public void shouldNotNull() {
Assertions.assertNotNull("Brazil");
}
}
In this version, there is Lambda support, such as Assertions.assertAll and Assertions.assertTimeout, which both expect an Executable interface, as an interface with one single method that is a functional interface:
public class JunitWitJava8Support {
@Test
@DisplayName("That breaks compatibility instead of @Test(expected = NullPointerException.class)")
public void shouldReturnNPE() {
Assertions.assertThrows(NullPointerException.class, () -> {
throw new NullPointerException();
});
}
@Test
public void shouldCheckErrorMessage() {
String errorMessage = "Error message";
NullPointerException exception = Assertions.assertThrows(NullPointerException.class, () -> {
throw new NullPointerException(errorMessage);
});
Assertions.assertEquals(errorMessage, exception.getMessage());
}
@Test
public void shouldShouldExecute() {
Assertions.assertAll(() -> {
Assertions.assertTrue(Collections.emptyList().isEmpty());
}, () -> {
Assertions.assertNull(null);
});
}
@Test
public void shouldGetResult() {
Process process = new Process();
String result = Assertions.assertTimeout(Duration.ofMillis(2000), process::process);
Assertions.assertEquals("Message result", result);
}
class Process {
String process() throws Exception {
Thread.sleep(1000);
return "Message result";
}
}
}
Extensions
Follow the documentation about the extension in JUnit 5:
In contrast to the competing Runner, @Rule, and @ClassRule extension points in JUnit 4, the JUnit Jupiter extension model consists of a single, coherent concept: the Extension API. Note, however, that Extension itself is just a marker interface.
Using a Mockito extension, you can use it integrated with JUnit, as in JUnit 4:
@ExtendWith(MockitoExtension.class)
public class ExtenseionMockitoTest {
@Mock
private Team team;
@BeforeEach
public void beforeEach() {
Mockito.when(team.getName()).thenReturn("Bahia");
}
@Test
public void shouldEquals() {
Assertions.assertEquals("Bahia", team.getName());
}
@Test
public void shouldInjectByParam(@Mock Team team) {
Assertions.assertNotNull(team);
}
interface Team {
String getName();
}
}
In this article, we're going to use your Extension, however, to CDI 2.0. Basically, this extension once has a field with @Inject it will inject using the SeContainer in CDI using the fields qualifiers.
public class CDIExtension implements TestInstancePostProcessor {
private static final SeContainer CONTAINER = SeContainerInitializer.newInstance().initialize();
private static final Predicate<Annotation> IS_QUALIFIER = a -> a.annotationType().isAnnotationPresent(Qualifier.class);
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws IllegalAccessException {
for (Field field : getFields(testInstance.getClass())) {
if (field.getAnnotation(Inject.class) != null) {
Annotation[] qualifiers = Stream.of(field.getAnnotations())
.filter(IS_QUALIFIER)
.toArray(Annotation[]::new);
Object injected = CONTAINER.select(field.getType(), qualifiers).get();
field.setAccessible(true);
field.set(testInstance, injected);
}
}
}
private List<Field> getFields(Class<?> clazzInstance) {
List<Field> fields = new ArrayList<>();
if (!clazzInstance.getSuperclass().equals(Object.class)) {
fields.addAll(getFields(clazzInstance.getSuperclass()));
} else {
fields.addAll(asList(clazzInstance.getDeclaredFields()));
}
return fields;
}
}
Testing the Recommendation System
In the previous article, we discussed a system recommendation using Neo4J and EE4J now it's time to test it. In the code, there is a connection to Neo4J. However, a dependency on an external database does not seem like a good strategy, so for tests, we can use a Neo4J embedded database. Furthermore, this step creates an implementation of GraphProducer with the embedded database, then overrides to the external database. CDI handles it using the Alternatives annotation with higher priority:
@ApplicationScoped
@Alternative
@Priority(Interceptor.Priority.APPLICATION+10)
public class EmbeddedSupplier implements GraphSupplier {
private Graph graph = Neo4jGraph.open(new File("").getAbsolutePath() + "/target/jnosql-graph");
@Override
@Produces
public Graph get() {
return graph;
}
}
Concluding the embedded database, the next step is to create tests integrated with CDI:
@ExtendWith(CDIExtension.class)
public class CityRepositoryTest {
@Inject
@Database(DatabaseType.GRAPH)
private CityRepository repository;
@Test
public void shouldFindById() {
Name name = Name.of("New York");
City city = new City(name.get());
repository.save(city);
assertTrue(repository.findByName(name.get()).isPresent());
}
@Test
public void shouldDeleteById() {
Name name = Name.of("New York");
City city = new City(name.get());
repository.save(city);
assertTrue(repository.findByName(name.get()).isPresent());
repository.deleteByName(name.get());
assertFalse(repository.findByName(name.get()).isPresent());
}
}
@ExtendWith(CDIExtension.class)
class BuddyServiceTest {
@Inject
private BuddyService service;
@Inject
private BuddyLoader loader;
@BeforeEach
public void setUp() {
loader.loadVertex();
loader.loadEdges();
}
@AfterEach
public void after() {
loader.clean();
}
@Test
public void shouldFindByTechnology() {
List<Buddy> javaDevelopers = service.findByTechnology(JAVA.getName());
assertAll(() -> {
assertFalse(javaDevelopers.isEmpty());
}, () -> {
assertEquals(2, javaDevelopers.size());
}, () -> {
Predicate<String> isJoao = "joao"::equalsIgnoreCase;
Predicate<String> isJose = "jose"::equalsIgnoreCase;
assertTrue(javaDevelopers.stream().map(Buddy::getDisplayName).allMatch(isJoao.or(isJose)));
});
}
}
References:
Opinions expressed by DZone contributors are their own.
Comments