JUnit is prominent member of the family of unit testing frameworks which is collectively known as xUnit that originated with SUnit
It is the most popular unit testing framework for the Java programming language, commonly used for Test Driven Development.
An understanding of Java Fundamental concepts:
Install a JDK (version 8 or above)
Setup an IDE for JAVA (e.g. IntelliJ IDEA Community Edition for Linux)
Once IntelliJ and the JDK are installed, start IntelliJ and create a Maven project using the quick start archetype
remove the base classes and default packages (optional for this tutorial) under the test.java and main.java packages
remove any existing junit packages added to the pom file by the project builder
Use the Maven repository website to reference the correct package to add into the maven project POM.xml file
use JUnit Jupiter (Aggregator) dependency package
Paste in the text in the text field into the project POM.xml and rebuild the project via the maven tools
Create a test class called CalculatorTest.class under the test.java.org.example package (or under a package of your own choice if you removed the default ones)
package org.example;
class CalculatorTest {
}
Add the JUnit @Test annotation which verifies expected behaviour when executed and import its parent package.
Add the JUnit @DisplayName annotation which sends a declared string as console output during runtime, when this test is executed.
Add a void protected method for addition of two numbers called add()
the test will assert if the sum of two numbers is equal to an expected result, at this point there has been no implementation of the actual application;
i.e. We are yet to create the Calculator.class, however following TDD, we need to write the directive test first, informing the implementor of the Calculator class that there is at least one method that can take two numbers, add them together and return this sum value.
All input types should be primitive doubles...
@Test
@DisplayName("Test that addition of two numbers results in correct sum")
void add() {
assertEquals(4, Calculator.add(2, 2));
}
Try adding another test this time create a test for subtraction, multiplication or division
You should end up with something like...
package org.example;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
@DisplayName("Test that addition of two numbers results in correct sum")
void add() {
assertEquals(4, Calculator.add(2, 2));
}
@Test
@DisplayName("Test that multiplication of two numbers results in correct product")
void multiply() {
assertAll(
() -> assertEquals(4, Calculator.multiply(2, 2)),
() -> assertEquals(-4, Calculator.multiply(2, -2)),
() -> assertEquals(4, Calculator.multiply(-2, -2)),
() -> assertEquals(0, Calculator.multiply(1, 0))
);
}
}
If you now run the test via the gutter play icons or via the build and run toolbar (below), it should be broken - which at this point is a SUCCESS!!!
You should see the failures for each line of the tests that reference the yet to be implemented Calculator class
Lets fix the test by creating the Calculator.class under the //src/main/java/org/example package path as follows:
package org.example;
import java.util.stream.DoubleStream;
public class Calculator {
static double add(double... operands) {
return DoubleStream.of(operands)
.sum();
}
static double multiply(double... operands) {
return DoubleStream.of(operands)
.reduce(1, (a, b) -> a * b);
}
}
The class has a method for addition, add(), that accepts an arbitrary number of arguments of type double, using the the DoubleStream method to take in the arguments as a stream and apply the sum() method to them.
A similar method has been constructed for multiplication called multiply(), this time using reduce() to take each element from the arguments fed into the method and multiply them via a lambda call, until all argument values have been exhausted.
Re-running the tests should now result in a positive test in the console window:
We now have a complete working solution that fixes our test using the test driven development methodology!
Test Setup
@BeforeAll
Use this annotation at the start of a test class to run a series of statements once, before all tests are executed
@BeforeAll
void initialSetup() {
System.out.println("@BeforeAll: This annotation runs once Before any of the tests are executed");
}
@BeforeEach
Use this annotation to execute statements immediately before each test is executed
@BeforeEach
void testSetup() {
System.out.println("@BeforeEach: This annotation runs immediately Before Each test is executed");
}
Test Clean Up
@AfterEach
Use this annotation to execute statements immediately after each test is executed
@AfterEach
void testCleanUp(){
System.out.println("@AfterEach: This annotation runs after each test completes execution");
}
@AfterAll
Use this annotation at the start of a test class to run a series of statements once, after all tests are executed
@AfterAll
void testRunCleanUp(){
System.out.println("@AfterAll: This annotation runs as the final step in the JUnit test runner runtime execution ");
}
Here is a simple example Annotations.class that puts all of these annotations together wrapping around two placeholder tests:
package org.example;
import org.junit.jupiter.api.*;
class Annotations {
/*
* SET UP TEST METHODS
* */
@BeforeAll
void initialSetup() {
System.out.println("@BeforeAll: This annotation runs once Before any of the tests are executed");
}
@BeforeEach
void testSetup() {
System.out.println("@BeforeEach: This annotation runs immediately Before Each test is executed");
}
/*
* TEST METHODS
* */
@Test
@DisplayName("Placeholder Test1: Placeholder for actual test")
void emptyTest1() {
System.out.println("This is a test @Test!");
}
@Test
@DisplayName("Placeholder Test2: Placeholder for actual test")
void emptyTest2() {
System.out.println("This is a test @Test!");
}
/*
* TEAR DOWN TEST METHODS
**/
@AfterEach
void testCleanUp(){
System.out.println("@AfterEach: This annotation runs after each test completes execution");
}
@AfterAll
void testRunCleanUp(){
System.out.println("@AfterAll: This annotation runs as the final step in the JUnit test runner runtime execution ");
}
}
If you were to run this test class as is you will get an error at runtime, as the annotated methods are static by default, however the exception provides the solution:
Add the statement below to the file declarations:
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
The class should now look like this:
package org.example;
import org.junit.jupiter.api.*;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class Annotations {
/*
* SET UP TEST METHODS
* */
@BeforeAll
void initialSetup() {
System.out.println("@BeforeAll: This annotation runs once Before any of the tests are executed");
}
@BeforeEach
void testSetup() {
System.out.println("@BeforeEach: This annotation runs immediately Before Each test is executed");
}
/*
* TEST METHODS
* */
@Test
@DisplayName("Placeholder Test1: Placeholder for actual test")
void emptyTest1() {
System.out.println("This is a test @Test!");
}
@Test
@DisplayName("Placeholder Test2: Placeholder for actual test")
void emptyTest2() {
System.out.println("This is a test @Test!");
}
/*
* TEAR DOWN TEST METHODS
**/
@AfterEach
void testCleanUp(){
System.out.println("@AfterEach: This annotation runs after each test completes execution");
}
@AfterAll
void testRunCleanUp(){
System.out.println("@AfterAll: This annotation runs as the final step in the JUnit test runner runtime execution ");
}
}
Re-running the test now will result in the following console output:
@BeforeAll: This annotation runs once Before any of the tests are executed
@BeforeEach: This annotation runs immediately Before Each test is executed
This is a test @Test!
@AfterEach: This annotation runs after each test completes execution
@BeforeEach: This annotation runs immediately Before Each test is executed
This is a test @Test!
@AfterEach: This annotation runs after each test completes execution
@AfterAll: This annotation runs as the final step in the JUnit test runner runtime execution
The output above (and below) shows the stage when each annotation is run during the test runner sequence...