Contact Us

Contact Us



Pleas confirm by checkbox


App Development

What is new in JUnit 5?

Author_img
By Arindam Nayak August 27, 2019

JUnit 5 is the new testing platform from Java, which consists of the following components. It requires Java 8 or above version.

JUnit Platform – It serves as a foundation for launching a testing framework on JVM. It also defines TestEngine API for developing a testing framework that runs on the platform.

JUnit Jupiter – It is the combination of the new extension for writing tests and extensions in JUnit 5.

JUnit Vintage – It provides a TestEngine for running JUnit 3 and JUnit 4 based tests on the platform.

Introduction:

In this article, I will explain the major new features of JUnit 5. To execute the test case, I have taken a simple application which is explained in the below section. I will walk through the test cases I added during that I will explain those features.

About the application:  The application is built using spring boot. It has a data access layer and the service layer. It does not have any controller as I mainly used this to demo test cases. It is a gradle based project. Here I have excluded junit4 and added junit5 using following snippet.

 

testImplementation('org.springframework.boot:spring-boot-starter-test') {
     exclude group: 'junit', module: 'junit'
 }
 testImplementation('org.junit.jupiter:junit-jupiter:5.5.1')

 

  • It has CRUD operation on employee DB. The employee has email, first name, last name.
  • On saving employee, it validates email of the employee.
  • While updating employee, it throws an error if given id of employee object is 0, if given employee uses email of another employee, if given email of an employee is not present. It permits to change firstname and last name only.
  • It fetches employee with id or gets all employees.
  • Deletes an employee by id.
  • Here I will be writing an integration test which interacts with DB and it uses spring context. Here are changes need in JUnit 5.
  • In JUnit4, we used to have “@RunWith(SpringRunner.class)”, but in jUnit5, it is replaced extension model, we have to annotate with “@ExtendWith(SpringExtension.class)”.
  • Here we have “BeforeEach”, “BeforeAll” , “AfterEach”, “AfterAll” annotation for testcase pre and postcondition. Name itself is intuitive to understand the purpose.

Following are some of the features of JUnit 5.

Lambda support in the assertion:

This facilitates to have a single assertion (assertAll) which can contain multiple assertions to evaluate individual conditions. Following is the snippet I have used for asserting all attributes of user.

 

@DisplayName("Valid employee can be stored")
 @Test
 public void validEmployeeCanBeStoredTest() throws EmployeeException {
   Employee savedEmployee = employeeService.save(employee);
   assertAll("All user attribute should be persisted",
       () -> assertEquals(employee.getFirstName(), savedEmployee.getFirstName(), "Firstname matched"),
       () -> assertEquals(employee.getLastName(), savedEmployee.getLastName(),"Lastname matched"),
       () -> assertEquals(employee.getEmail(), savedEmployee.getEmail(), "Email matched"),
       () -> assertTrue(savedEmployee.getId() > 0,"Employee ID generated"));
 }

 

Here you can see that using a single assertAll we can have a different type of assertions.

Note: DisplayName helps to get some descriptive information about the test case like following.

 

Parameterize test

It facilitates to run a testcase multiple times with different arguments. Instead of @Test, it is annotated with @ParameterizedTest. It must declare at least one source of the parameter for the argument. Here is some valid source type – “ValueSource”, “Null Empty Source”, “EnumSource”, “MethodSource”, “CsvSource”, “CsvFileSource”, “ArgumentSource”.

I will explain about ValueSource and MethodSource, rest all are not in the scope of this article and I might cover them in a separate article. Following is the snippet which I used for testing valid and invalid emails. You might have guessed it since we have to test it multiple times with different value this comes to be a good candidate to demonstrate.

 

@Tag("unit")
 class InvalidEmailTest {
 
   @DisplayName("Test with valid")
   @ParameterizedTest(name = "email = {0}")
   @ValueSource(strings = { "this@email.com", "email.example@example.com", "demo@local.co.in" })
   void validEmails(String email) {
     assertTrue(EmailValidator.isValidEmail(email));
   }
 
   @DisplayName("Test with invalid")
   @ParameterizedTest(name = "email = {0}")
   @MethodSource("invalidEmailsProvider")
   void invalidEmails(String email) {
     assertFalse(EmailValidator.isValidEmail(email));
   }
 
   private static Stream<String> invalidEmailsProvider() {
     return Stream.of("this@email", "email@example.", "local.co.in");
   }
 
 }

 

ValueSource is pretty much straight forward, here we need to pass a different value as an array, and it will automatically pass to test method.

MethodSource helps to customize the arguments using Stream. It helps to pass a complex object to the method.

Note: Here we don’t have a display name, rather we have specified it as “@ParameterizedTest(name = “email = {0}”)”, it will help to nicely show the testcase execution like following.

 

Nested test case

It helps to build a kind of test suite which contains more meaningful and related group of the test in single class. Here is the code snippet.

 

@Transactional
 @SpringBootTest()
 @ExtendWith(SpringExtension.class)
 @DisplayName("Invalid employee test")
 @Tag("integration")
 class InvalidEmployeeCreateTest {
 
   private Employee employee;
 
   @Autowired
   EmployeeService employeeService;
 
   @Nested
   @DisplayName("For invalid email") class ForInvalidEmail {
 
     int totalEmployee = 0;
 
     @BeforeEach
     void createNewEmployee() {
       totalEmployee = employeeService.findAll().size();
       employee = new Employee("TestFirstName", "TestLastName", "not-an-email");
     }
 
     @Test
     @DisplayName("throws invalid email exception")
     void shouldThrowException() {
       assertThrows(EmployeeException.class, () -> employeeService.save(employee));
     }
 
     @Nested
     @DisplayName("when all employee fetched") class WhenAllEmployeeFetched {
 
       @Test
       @DisplayName("there should not be any new employee added")
       void noEmployeeShouldBePresent() {
         assertTrue(employeeService.findAll().size() == totalEmployee, "No new employee present");
       }
     }
   }
 }

 

Here is the output for this.

 

 

Explanation:

It starts with ForInvalidEmail nested class, it contains “BeforeEach”  which is invoked before each of test case, there we are creating an employee object which contains an invalid email. This class contains single test method “shouldThrowException” which checks if we try to save that employee object it should throw an exception.

In “ForInvalidEmail”  nested class, I have added one more nested class named “WhenAllEmployeeFetched”. This nested class contains a method which ensures no new employee record is created.

Here, you can see that how we grouped 2 related tests ( should throw an exception and no employee should be added) using nested class.

Dynamic Test

Earlier, we have only @Test which is static and specified at compile time. Their behavior can’t be changed in runtime. In junit5, new annotation is added for this @TestFactory.  It is factory of the test case, and it must return a single DynamicNode or Stream instance.

Any Stream returned by a @TestFactory will be properly closed by calling stream.close(), making it safe to use a resource such as Files.lines().

As with @Test methods, @TestFactory methods must not be private or static and may optionally declare parameters to be resolved by ParameterResolvers.

A DynamicTest is a test case generated at runtime. It is composed of a display name and an Executable. Executable is a @FunctionalInterface which means that the implementations of dynamic tests can be provided as lambda expressions or method references.

Following is the code snippet using this.

 

@Transactional
 @SpringBootTest()
 @ExtendWith(SpringExtension.class)
 @Tag("integration")
 class EmployeeUpdateTest {
 
   private List<Employee> employees = Arrays.asList(
       new Employee("valid", "name", "email1@exmaple.com", 1),
       new Employee("invalid", "id", "email2@exmaple.com", 0), // Invalid employee
       new Employee("uses", "other email", "email1@exmaple.com", 3), // Invalid as it uses 1st employee email
       new Employee("email", "does not exist", "email4@exmaple.com", -1)); // Non-existent email can't be updated
   @Autowired
   EmployeeService employeeService;
   @TestFactory
   Stream<DynamicTest> dynamicEmployeeTest() {
     return employees.stream()
         .map(emp -> DynamicTest.dynamicTest(
             "Update Employee: " + emp.toString(),
             () -> {
               try {
                 assertNotNull(employeeService.update(emp));
               } catch (EmployeeException e) {
                 assertTrue(e.getMessage().toLowerCase().contains("error:"));
                 assertTrue(emp.getId() != 1); // All employee except 1 should fail
               }
             }
         ));
   }
 }

 

Explanation: Here we have dynamicEmployeeTest method which is annotated with @TestFactory and it returns stream of DynamicTest. As you can see, here we have a mix of some employees, where only 1 record is valid for update, rest all employees have some problems mentioned in the code comment. This dynamic method actually emits 4 test case for which 1st employee goes through try block and rest all falls through the exception block.

Advantage: It shortens the codebase to test. Using JUnit 4, we have to write 4 different test cases to verify such behaviour.

Here is the output when running from IDE.

 

 

Note: This does not follow the lifecycle of testcases as those testcases are dynamically generated. So, if you have before each / BeforeAll/ AfterEach/ AfterAll methods, those won’t be accessed or executed for testfactory methods.

Here are some more features of JUnit 5:

  • Conditional test execution.
  • Test Execution order
  • DI for constructor and methods
  • Repeated test
  • Timeout test.

References:

Related posts
Angular — How to render HTML containing Angular Components dynamically at run-time
App Development

Angular — How to render HTML containing Angular Components dynamically at run-time

By shekhar.wagh May 05, 2021
Part-3: Building a bidirectional-streaming gRPC service using Golang
App Development

Part-3: Building a bidirectional-streaming gRPC service using Golang

By shekhar.wagh April 22, 2021
A Step-by-Step Guide to Easy Android in-App Review Setup.
App Development

A Step-by-Step Guide to Easy Android in-App Review Setup.

By shekhar.wagh April 16, 2021
Part -2: Building a unidirectional-streaming gRPC service using Golang
App Development

Part -2: Building a unidirectional-streaming gRPC service using Golang

By shekhar.wagh April 15, 2021
How to Integrate Firebase Authentication for Google Sign-in Functionality?
App Development

How to Integrate Firebase Authentication for Google Sign-in Functionality?

By shekhar.wagh April 09, 2021
Part-1: A Quick Overview of gRPC in Golang
App Development

Part-1: A Quick Overview of gRPC in Golang

By shekhar.wagh April 07, 2021
Publish Your Android Library on JitPack for Better Reachability
App Development

Publish Your Android Library on JitPack for Better Reachability

By shekhar.wagh April 02, 2021
How to Use Firebase Remote Config Efficiently?
App Development

How to Use Firebase Remote Config Efficiently?

By shekhar.wagh March 26, 2021
How to simplify Android app distribution with Fastlane and improve workflow?
App Development

How to simplify Android app distribution with Fastlane and improve workflow?

By shekhar.wagh March 18, 2021
Google Play Instant Run Integration
App Development

Google Play Instant Run Integration

By shekhar.wagh September 09, 2019

Stay updated

Get the latest creative news from Fubiz about art, design and pop-culture.