How to write JUnit test cases

Step-by-step guide on how to write and run JUnit test cases. Use BrowserStack to test on real devices.

Get Started free
Home Guide How to write JUnit test cases

How to write JUnit test cases

JUnit is one of the most popular testing frameworks for Java applications. It enables developers to write and run repeatable tests to ensure each component of a program functions correctly. Writing JUnit test cases helps catch bugs early, supports refactoring with confidence, and promotes the development of reliable and maintainable code.

JUnit has evolved over time. JUnit 4 introduced key features like annotations (@Test, @Before, @After) and assertions (assertEquals, assertThrows). JUnit 5 builds on this foundation with new annotations like @BeforeEach and @AfterEach and improved features like more flexible test lifecycle management and better integration with modern development tools.

Read this guide to learn how to write JUnit test cases, their importance, implementation steps, and best practices.

Java Unit Testing Frameworks and JUnit

Unit testing frameworks in Java provide an efficient tool for writing, organizing, and executing test cases. This is important for maintaining code quality and detecting issues early in the software development cycle. Some popular frameworks include JUnit, TestNG, Mockito, AssertJ, and Hamcrest.

JUnit uses an annotation-based syntax that makes writing and managing tests easy. With annotations like @Test for marking test methods, and @BeforeEach or @AfterEach for setting up and cleaning test environments, JUnit provides a clean structure for test execution.

The release of JUnit 5 brings improvements like better modularity, support for modern Java features like lambdas, and a more powerful extension model.

Why use JUnit for unit testing Java applications?

JUnit is a widely used testing framework for Java that simplifies unit testing and ensures code reliability. Here’s why JUnit is essential for Java applications:

  • Automated & Repeatable Tests: JUnit allows developers to automate test execution, ensuring consistent results and early bug detection.
  • Simple & Intuitive API: With easy-to-use annotations like @Test, writing and managing test cases becomes straightforward.
  • Seamless Integration: It works well with IDEs like Eclipse and IntelliJ and build tools like Maven and Gradle, making it CI/CD-friendly.
  • Supports TDD: Encourages Test-Driven Development, leading to modular, maintainable, and well-structured code.
  • Powerful Assertions & Annotations: Provides rich assertions and lifecycle methods for efficient test execution.

JUnit’s structured approach makes it a preferred choice for unit testing, ensuring Java applications are stable, maintainable, and bug-free.

What are JUnit Test Cases?

JUnit test cases are unit tests written using the JUnit framework to verify the functionality of individual components in a Java application.

These tests help ensure that methods and classes produce expected results, improving code reliability. JUnit provides annotations like @Test, @Before, and @After to structure test cases efficiently.

Importance of JUnit Test Cases

Below are the key reasons why JUnit test cases are important:

  • Ensures Code Reliability: Detects bugs early by validating code behavior.
  • Supports Test-Driven Development (TDD): Helps developers write test cases before implementing code.
  • Automates Testing: Reduces manual effort by running tests automatically.
  • Enhances Code Maintainability: Encourages modular and error-free code by enforcing best practices.
  • Facilitates Continuous Integration: Works seamlessly with CI/CD pipelines for smooth deployments.

How to write JUnit Test Cases

JUnit tests are written in the same manner as Java projects; however, while writing tests, you must be aware of some JUnit annotations. JUnit annotations are blocks of predefined text that the Java API provides for better structure and readability.

Here is an example of a few frequently used JUnit annotations that come in handy in this tutorial on Java JUnit testing.

  1. @Test – to identify the actual test case
  2. @Before – to execute some statement before each test case
  3. @After – to execute some statement after each test case
  4. @Ignores – to ignore some statement during the execution of the test
  5. @BeforeAll – to execute some statement before all the test cases
  6. @AfterAll – to execute some statement after all the test cases

Step-by-Step Guide: Writing a JUnit Test Case with Example

Now, consider a practical example to understand Java Junit testing. This example will conduct a simple test using JUnit where:

  • First, assign some value to a string, and
  • Then, with the help of the assert keyword, check if the value assigned matches our intended value.

Prerequisite for Junit Test Cases

Here are the prerequisites required for running JUnit test cases:

  1. You must have Java JDK installed on your system. To check if Java is installed on your system, run the command java –version in the Command prompt. If it is installed, it will display the Java version.
  2. Any IDE of your choice. In this example, using Eclipse 2022.

Steps to Write Junit Test Cases

Some of the important steps to be followed for executing JUnit 5 test cases are:

Step 1: Create a Maven project 

To create a Maven project, open Eclipse and then complete the following steps:

1. Click the File option

2. Hover over New

3. Click the Project option

4. Select the Maven project

5. Enter the Group ID and Artifact ID of your choice and proceed.

6. Your Maven project is created.

Step 2: Add Maven dependencies 

In this step, you need to add some dependencies that will help you achieve JUnit testing. To do so, add a JUnit dependency from the Maven marketplace by inserting it into the pom.xml file that you got on our Maven project.

Add this script inside the dependencies tag in the pom.xml file.

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version> <!-- or the latest version -->
<scope>test</scope>
</dependency>

Step 3: Create a new class file 

Now, follow the following steps to create a new class file where you will write our Junit test.

1. Right-click the folder src/test/java

2. Hover over New

3. Click Class and create a file by entering a name of your choice

Step 4: Writing the Test Script 
Now the final part is to write the JUnit test after setting up the project. Open the previously created test class file inside the src/test/java folder.

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

public class BrowserStackJunit {
String message = “BrowserStack is the intended message”;

@Test
public void testMessage() {
System.out.println(“Inside testMessage()”);
assertEquals(“BrowserStack is the intended message”, message);
}

Step 5: Executing JUnit test 

Once the test is written, right-click and select Run as > JUnit test.

failed test junit5

  • If the test is successful, the left panel will be highlighted green.
  • If it fails, it will be indicated in red, and the error log will be displayed on the console.

How To Filter JUnit Tests Using Tags?

JUnit 5 allows you to organize test cases into categories and run specific groups of tests together using the tags feature. A common use case is to label tests as unit, integration tests, or system tests and execute them at different stages in the CI pipeline.

1. Adding Tags to Tests

In JUnit 5, you can use the @Tag annotation to assign categories to tests. This helps organize tests and allows for running specific groups based on tags.

import org.junit.jupiter.api.Tag;

import org.junit.jupiter.api.Test;

public class UserServiceTest {

@Test
@Tag("fast")
void quickTest() {
}
@Test
@Tag("slow")
void longRunningTest() {
}
}

2. Filtering by Tags using Maven

By configuring the maven-surefire-plugin in your pom.xml, you can include or exclude specific tests based on their tags. This makes it easier to run only relevant tests during your build or CI process.

<build>

  <plugins>

<plugin>

   <groupId>org.apache.maven.plugins</groupId>

   <artifactId>maven-surefire-plugin</artifactId>

   <version>3.0.0</version>

   <configuration>

     <groups>fast</groups>      <!-- Only run tests tagged with "fast" -->

     <!-- <excludedGroups>slow</excludedGroups> --> <!-- Exclude "slow" tests -->

   </configuration>

</plugin>

  </plugins>

</build>

3. Running Tests with Tags via Command Line

You can filter tests by tags directly from the command line using Maven to allow selective test execution:

mvn test -Dgroups=fast

To exclude a tag:

mvn test -DexcludedGroups=slow

Best Practices for writing JUnit Test Cases

Some of the best practices for writing JUnit test cases are:

1. Test on Real Devices

While unit tests are useful for initial logic verification, they can’t replicate issues that arise on real devices. Real-device testing identifies problems like UI inconsistencies and performance issues and ensures compatibility across different platforms.

BrowserStack Automate provides access to 3,500+ real devices and browsers to run tests under real user conditions. You can run unit tests with Java and Selenium. By integrating BrowserStack with your CI/CD pipeline, you can trigger JUnit test cases automatically during the build process to ensure consistent test execution across real devices.

BrowserStack Automate Banner

2. Keep Your Unit Tests As Small As Possible

Small and focused unit tests are easier to read, maintain, and debug when they fail. Here’s how you can write a small unit test that checks a specific behavior of the User class:

@Test

void shouldReturnFullName() {

User user = new User("Ayush", "Singh");

assertEquals("Ayush Singh", user.getFullName());

}

In this example, the test is focused only on verifying the getFullName method, making it easier to identify issues quickly.

3. Add Clear Messages to Assertions

Proper and detailed messages should be added to assertions to understand what went wrong when a test fails and make debugging faster. Here’s how to add a clear message to your assertions:

assertEquals("Names should match", "Ayush Singh", user.getFullName());

In this example, if the test fails, you will immediately know the issue is a mismatch between the expected and actual names.

4. Use the Correct Assert Method and Argument Order

JUnit assertions follow the format assertEquals(expected, actual), which helps prevent confusion and improve readability.

Here’s an example of the correct and incorrect usages of assertEquals:

// Correct

assertEquals("Expected output did not match", 10, calculator.add(5, 5));



// Avoid this (incorrect order)

assertEquals(calculator.add(5, 5), 10);

5. Reduce Code Duplication

To avoid repeating setup code in every test method, use the @BeforeEach annotation to initialize common test data or objects. This makes your tests more maintainable and readable.

For example, if you’re testing methods that require a user object, set it up once using @BeforeEach:

private User user;



@BeforeEach

void setUp() {

user = new User("Ayush", "Singh");

}



@Test

void shouldReturnFirstName() {

assertEquals("Ayush", user.getFirstName());

}

This setup ensures that the user object is available for all tests without the need to recreate it in each individual test method.

6. Use Annotations Wisely

JUnit provides powerful annotations (@Test, @BeforeEach, @AfterEach, @BeforeAll, @AfterAll) to manage test lifecycles. Use them to organize and optimize test execution.

For example, use @BeforeAll to initialize resources that are needed before any test runs:

@BeforeAll

static void initDatabase() {

}



Similarly, @AfterEach can be used to tear down resources after each test method:



@AfterEach

void tearDown() {

}

Advanced Techniques for Unit Testing with JUnit

The advanced features of JUnit help write more flexible, powerful, and detailed test code. These techniques allow covering edge cases, improving test readability, and handling dynamic testing conditions more effectively.

1. Checking for Exceptions

JUnit provides an easy way to verify that your code throws expected exceptions using assertThrows.

@Test

void shouldThrowExceptionWhenDividingByZero() {

assertThrows(ArithmeticException.class, () -> {

     int result = 10 / 0;

});

}

2. Parameterized Testing

This allows you to run the same test multiple times with different inputs.

@ParameterizedTest

@ValueSource(strings = {"racecar", "level", "madam"})

void shouldRecognizePalindromes(String word) {

assertTrue(StringUtils.isPalindrome(word));

}

3. Using Assumptions

Assumptions are useful for environment-specific or conditional logic, as they skip tests when certain conditions aren’t met.

@Test

void shouldRunOnlyOnDevEnvironment() {

assumeTrue("dev".equals(System.getProperty("env")));

}

4. Disabling Tests

This is used to disable a test temporarily. It’s helpful when a test is known to be broken or under revision.

@Test

@Disabled("Fixing a bug related to date formatting")

void shouldFormatDateCorrectly() {

}

5. Nested Test Classes

JUnit 5 allows for the creation of Nested classes for group-related tests and makes test suites more readable and logically structured. Here’s how to use it.

class BankAccountTest {



@Nested

class WhenNewAccount {



     private BankAccount account;



     @BeforeEach

     void setUp() {

         account = new BankAccount();

     }



     @Test

     void balanceShouldBeZero() {

         assertEquals(0, account.getBalance());

     }

}

}

6. Timeout

You can set a duration for the test to complete within a specific time frame using assertTimeout or @Timeout.

@Test

@Timeout(2)

void shouldCompleteInTwoSeconds() throws InterruptedException {

Thread.sleep(1500);

}

Talk to an Expert

Useful Resources for JUnit

Conclusion

JUnit 5 is an open-source unit-testing framework in the world of Java development. JUnit is generally used to write automated tests. The objective of unit tests is to be done in conjunction with the development of the software, so you can run them early in the development process in order to reduce errors at the same time.

Use BrowserStack to test JUnit 4 and JUnit 5 test cases on real devices and browsers. It supports parallel testing and can be integrated with your CI/CD tools to trigger tests with every build. You can also scale your testing by running multiple tests simultaneously to reduce overall test time.

Try BrowserStack for Free

Tags
Automation Testing Selenium Website Testing

Get answers on our Discord Community

Join our Discord community to connect with others! Get your questions answered and stay informed.

Join Discord Community
Discord