» Home » Java » JUnit 5 Introduction for TDD Development in Java

JUnit 5 Introduction for TDD Development in Java

by Imran Shaikh
Published: Last Updated on 2486 views
JUnit 5 Introduction for TDD Development in Java

Hey, Tea Lovers! This post is the start of the new series of posts on TDD development in Java. This will be a quick introduction to JUnit 5. It won’t be about JUnit 4 vs JUnit 5 instead, we will be focusing entirely on Junit 5 and how as a beginner, you can start working on it right off the bat. Let’s jump right into it.

I will keep on adding more posts on the topic such as Best practices, tips, and tricks, advanced testing, and many more. So please subscribe to our newsletter for new updates.

You can follow me on social media via @coderstea on TwitterLinkedinFacebook, or Instagram. We also share high-quality videos about programming on our Youtube channel. You can also publish your post on CodersTea.com, just share your thought on Contact Us or let us know in the comments.


Why Testing or TDD

To keep up with the demand for continuous delivery and integration, we need to make sure that this continuity isn’t hampering the quality of the product. We should thoroughly test the changes. Not only the new ones but make sure that old ones are not broken. Using TDD or Test Driven Development we can reduce the chances of forgetting to test or breaking any parts of the code, or even more bring down the whole product.

In Java JUnit 5 has brought a lot of new changes to help us do TDD Development. Let’s look at them one by one.

Requirment for TDD using Junit 5

First of all. I will be using a Maven project. For Java, I will use Java 8 but it works with any newer java versions. I will create another one for Java 16 test using record. In the project, I will add Junit 5 dependency.

But to understand what’s happening, you must be familiar with Maven (basic), Java, and Java annotations. If you have worked with any testing framework that would be a bonus.

What is Junit 5 and How its Structured

JUnit 5 is a major update of JUnit 5. Unlike previous versions, Junit 5 is a multi-module framework. It consists of the platform, engine, and vintage Junit modules.

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

JUnit Platform sets up the JVM to run the test engines. With this, it gets flexible to create your testing framework or add 3rd party plugins. JUnit Jupiter provides the annotations for testing. We will look at the common one in a bit. The last one, JUnit Vintage, is to run older versions of JUnit, 3 and 4, on the JUnit 5 platform.

Install Junit 5 in Java Project

To add the Junit 5 in the project, add the following JUnit 5 dependency in the build file. I have put both maven and Gradle dependency for version 5.8.0-M1.

Maven project

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.8.0-M1</version>
    <scope>test</scope>
</dependency>

You have to name your test classes with the following naming conventions, otherwise, it won’t get picked up by maven.

You can also add the Maven Surefire plugin to customize which test classes to pick. It will go under the build > pluginManagement > plugins tag.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M5</version>
</plugin>

Your first Test Using Junit 5

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class JUnit5ExampleTest {
  @Test
  void yourFirstTest(){
    int a = 5;
    int b = 10;
    assertEquals(15, a + b);
  }
}

There are multiple things in this small block. I will explain it to you in brief. First, the class JUnit5ExampleTest is in the test folder of the project i.e. src/java/test. Simple class, nothing fancy here except no need of public keywords for class. The second thing is @Test an annotation on the yourFirstTest method. This tells JUnit that this function is to be tested. The third one is the assertEquals. This is a static function from Assertion the class of JUnit 5. It simply says that 15 should be the output when a + b is executed, Expected and Actual value respectively.

@Test

As shown in the above example, @Test annotation is used to mark the method as testable. These methods will be picked for testing by JUnit.Nothing fancy, similar to older versions.

@Test
void thisTestWillRun(){
  assertEquals(15, 12 + 3);
}

void thisWillNotRun(){
  assertEquals(15, 12 + 3);
}

Assertions

The most important part of any testing framework is the assertion. This simply stops the execution of the program in case the given output is not what we expected. This helps us to identify the problem with the code. JUnit 5 assertion methods are static and are part of org.junit.jupiter.api.Assertions the class. It has overloaded methods assertEquals with all the primitive and Objects as parameters for expected and actual parameters.

The new thing in JUnit 5 is the added support for Lambdas. This helps lazily initialize the values, increasing the overall time.

@Test
void assertUsingLambda(){
  assertTrue( 5 > 1, () -> "This message will be lazily loaded");
}

@BeforeEach and @BeforeAll

Most of the time you need to set up a few things before running the test, such as creating the testing class’s object. @BeforeAll and @BeforeEach help you to do exactly that. The function annotated with this is executed first. There is a difference between @BeforeAll and @BeforeEach. @BeforeAll will be a static function that will run when the test class is loaded, and it will run exactly once before all the test cases. On the other hand, @BeforeEach will be executed before each test case. @BeforeAll can be set up to create objects which will be required throughout the tests. @BeforeEach can be used for updating something or resetting an object or creating a new one at each iteration.

@BeforeAll
static void itWillRunBeforeAllTests(){
  System.out.println("This will run exactly once before all test cases");
}

@BeforeEach
void itWillRunBeforeEachTests(){
  System.out.println("This will run before each test cases");
}

@AfterAll and @AfterEach

These annotations are the opposite of @Before* annotations. As the name suggests, @AfterAll runs after all the tests cases are complete. @AfterEach is executed after each test case is complete.

@AfterAll
static void itWillRunAfterAllTests() {
  System.out.println("This will run exactly once after all test cases");
}

@AfterEach
void itWillRunAfterEachTests() {
  System.out.println("This will run after each test cases");
}

Again, please note that @AfterAll and @BeforeAll annotated methods must be static.

@DisplayName : Junit 5 Annotation

When testing a method, you have to name the testing method in such a way that it tells you what you are testing. Its considered one of the best practices when writing tests. Generally, it looks something like divideTest_WhenDividerIs0_ShouldReturn1. This name gives a lot of information about the test we are about to see or write. It’s a test for divide() and when we pass 0 as a divider it must return 0, which is the condition being tested.

But Most of the time name keeps getting bigger. So the solution is to use @DisplayName annotation. With this, you can write about the test, which can contain spaces, special characters, even emojis. This text will be shown in the console whenever tests are executed.

@Test
@DisplayName("Test Divide ✨🎠. It should return 1 if we pass 0 as divider.")
void divideTest_WhenDividerIs0_ShouldReturn1() {
  assertEquals(1, divide(11, 0));
  assertEquals(2, divide(16, 8));
}

int divide(int number, int divider) {
  if (divider == 0) return 1;
  return number / divider;
}

As you can see I only added spaces but also emojis. To get the most out of test cases’ names, you first have to name the actual methods in a better way. You can use the best practices described in the post. Java Naming Conventions: What’s In a Name?

Assumtions in Junit 5

Sometimes, before running tests you need to make sure certain conditions are fulfilled. It can be anything that your test required. It can be an object not being empty, or some value is set among others. In these scenarios, you don’t want to execute tests as you know those tests will fail since the requirement is not fulfilled.

With Assumptions in Junit 5, you can tell Junit to first check for these conditions and then only execute further. It could be done with, methods of Assumptions class, assumeTrue()assumeFalse(), and assumingThat() for true, false, and lambda respectively. If this condition fails an error will be thrown and tests are aborted.

For example, suppose you don’t want to run the test unless you have a system environment variable available named CODERSTEA whose value is the coderstea.in. You can do so with the following.

@Test
void testEnvVariable(){
  System.out.println("Checking if CODERSTEA variable is set in system environment");

  assumeTrue(System.getenv().containsKey("CODERSTEA"),
          "You must set CODERSTEA in your system env variable.");
  System.out.println("This won't run until you set CODERSTEA in you system");
}

Conclusion for Junit 5

I hope this will help you get started with JUnit 5. As I said this is just the beginning of the TDD development series that I will be writing on. Please be updated about them via Social Media or subscribe to our newsletter. The code for this tutorial is available on GitHub.

See you in the next post. HAKUNA MATATA!!!

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More

0
Would love your thoughts, please comment.x
()
x