Writing Nested Tests In JUnit 5

OVERVIEW

Software testing, as everyone knows, is a very critical phase in the software development process. It is essential to test the software's working before it is delivered to the end user.

We need to test the software to ascertain that it is doing what it is expected to do and, more importantly, not doing what it is not intended to do.

Now, testing can be done manually and in an automated way. Since manual testing is more time-consuming and prone to human errors, automating the software testing process is recommended, which eventually provides fruitful results in the long run. Hence unit testing is performed to get faster feedback and fix the issues before we move the Software build to a different environment, like QA, user acceptance testing (UAT), etc.

When it comes to testing, there are multiple checks that we need to perform on every developed feature. Some features are simple and straightforward, but some may be complex and require a lot of effort to test.

For testing complex features, we would be required to break up the complex logic into simple tests and run them, as it will help us test the feature in depth without missing anything.

Consider an example of a chatting website where you must register and log in with the correct credentials to chat with another registered user.

In this example, registration and login are important scenarios that need to be covered in testing. Again, there would be a scenario where the user would auto login once he completes the registration process.

These scenarios can be segregated into registration and login scenarios. Accordingly, in both scenarios, negative and positive tests would be required to be written to test the feature in depth.

So, one whole registration scenario would be written, with multiple tests, including negative tests and another for login tests. Now, if we could group these tests and set them as registration tests and place all the tests of the registration feature under it and likewise for the login feature as well, it would be easy for us to identify which tests caused the actual failure.

Here, writing nested tests using JUnit 5 features would come in handy.

In this JUnit testing tutorial on JUnit 5 nested tests, we will discuss writing nested tests using the JUnit 5 library. Before we move to code, let us first understand nested classes and why they are used.

Table of Contents

What is JUnit?

JUnit is a Java-based unit testing framework. It is a family of unit testing frameworks collectively known as xUnit. Java Developers use this framework to write and execute automated regression tests.

Regression testing is a type of software testing that checks if the recent changes made to the code do not adversely affect the previously written code.

JUnit 5 is the next generation of JUnit. The goal is to create an up-to-date foundation for developer-side testing on the JVM. This includes focusing on Java 8 and above, as well as enabling many different styles of testing. [Source]

As JUnit is a unit testing framework, it has some cool and advanced features that can be used by the developers to unit test the code they have written. It helps them test the complex logic by breaking it into simple tests and allows them to cover all cases as required for the feature.

One such feature of @Nested annotation in JUnit. Let’s dive deep into this and learn more about it.

What is @Nested annotation in JUnit 5

A nested class is an inner non-static class placed inside another class and arranged in a hierarchical structure and can share setup and state with the instance of the outer class. It is similar to nested inner classes in Java. @Nested annotation is a part of the Jupiter package.

Java inner class or nested class is a class that is declared inside the class or interface.

LambdaTest

When do we need a @Nested class?

There are scenarios I have discussed further in this blog on JUnit 5 nested tests that we would need to cover in the tests. However, those are based on complex logic. Also, we need to break those complex logic into small pieces of simple tests, so we don’t mess up with the coverage and ensure that we cover all the bits and pieces of the logic in the unit tests.

Let me give you an example for better understanding - Consider an eCommerce app like LambdaTest E-Commerce Playground Website where registration and login functionality is mandatorily required, without which the user won't be able to purchase any product and checkout.

Once the code is written, we need to unit test it to check the validation of the features.

 LambdaTest E-Commerce Playground Website where registration and login functionality

When we come to the login scenarios, we would be required to test multiple scenarios, like

  • logging in with an invalid password.
  • invalid username.
  • blank username and password, and so on.

It would be best to group all these scenarios and write them, as it will help create clean and maintainable code. We can achieve this by using the @Nested annotation of JUnit. We will discuss this later in this blog on JUnit 5 nested tests, where we will create some example code for these scenarios.

Using @Nested annotation, the result looks even better than the one with the usual single class.

Since we have been discussing @Nested annotation for quite some time now, let’s get to understanding “How to use the @Nested annotation?”

How to use @Nested annotation?

@Nested annotation can be used inside the test class using the annotation on the inner class.

Checkout the below code snippet for detail:

public class EcommerceAppTest {

@Test
public void testOne() {
    // Some code here...
}
@Test
public void testTwo () {
   // some code here…
}

@Nested
class nestedTest {

    @BeforeEach
    void beforeEachMethod() {
         System.out.println ("This method will be called in nested class before each test");
    }

    @Test
    void nestedTestOne () {
        System.out.println ("This is a nested test one");
    }
    @Test
    void nestedTestTwo () {
        System.out.println ("This is a nested test two");
    }

    @AfterEach
    void afterEachMethod() {
         System.out.println ("This method will be called in nested class after each test");
    }

}
}

As you see in the example above, we need to create an inner class and put @Nested annotation above it, and we are all set to write tests using the nested annotation feature.

The following is the order of execution of the above class:

  • testOne()
  • testTwo()
  • Nested class
    • beforeEachmethod()
    • nestedTestOne()
    • afterEachMethod()
    • beforeEachmethod()
    • nestedTestTwo()
    • afterEachMethod()

Methods with @BeforeEach and @AfterEach annotations inside the nested class will be executed for each test inside the nested class only.

We can also use the @BeforeAll and @AfterAll annotations inside the nested class; however, to do so, we would need to add the @TestInstance (TestInstance.Lifecycle.PER_CLASS) annotation above the nested class.

Checkout the below code snippet for detail:

@TestInstance (TestInstance.Lifecycle.PER_CLASS)
public class SampleClass {

   @BeforeAll
   public void beforeAllTest () {
       System.out.println ("This is the before all method from outer class");
   }

   @Test
   public void testOneFromOuterClass () {
       System.out.println ("This is test one from outer class");
   }

   @Test
   public void testTwoFromOuterClass () {
       System.out.println ("This is test two from outer class");
   }

   @Nested
   @TestInstance (TestInstance.Lifecycle.PER_CLASS)
   class nestedClass {
       @BeforeAll
       public void beforeAllNestedTest () {
           System.out.println ("This is before all method from nested class!");
       }

       @Test
       public void testOne () {
           System.out.println ("This is first test of nested class!");
       }

       @Test
       public void testTwo () {
           System.out.println ("This is second test of nested class!");
       }
       @AfterAll
       public void afterAllNestedTest () {
           System.out.println ("This is the after all method from nested class");

       }
   }

   @AfterAll
   public void afterAllTest () {
       System.out.println ("This is the before all method from outer class");
   }
}

Here is how the results will look once the above class is executed:

BeforeAll annotation will get executed first from the outer class

The method with @BeforeAll annotation will get executed first from the outer class. After that, the methods with @Test annotation from the outer class will get executed.

Next, the class with @Nested annotation will get executed in which first of all, the method with @BeforeAll annotation will get executed, then methods with @Test annotation inside the nested class will be executed, and finally, the method with @AfterAll annotation from nested class will be executed

In the end method with @AfterAll annotation from the outer class will get executed.

Let’s now check out the important rules we need to follow for using the @Nested annotation.

...

2M+ Devs and QAs rely on LambdaTest

Deliver immersive digital experiences with Next-Generation Mobile Apps and Cross Browser Testing Cloud

Rules for using @Nested annotation

Following are the rules that need to be followed while using the @Nested annotation:


  • All nested test classes must be non-static inner classes.
  • An outermost class can never be annotated with @Nested. It has to be an internal class or classes inside this outermost class that needs to have @Nested annotation.
  • @Nested annotation is used above the nested class and not on a specific test.
  • We have to annotate our nested test classes with the @Nested annotation. This annotation ensures that JUnit 5 recognizes our nested test classes.
  • The nested class has to be non-static, i.e., none of the methods within the Nested class can be static.
  • There is no limit to the depth of the class hierarchy.
  • By default, a nested test class can contain test methods, one @BeforeEach method, and one @AfterEach method.
  • To make the methods under the @BeforeAll and @AfterAll annotation work in a nested class, we need to add the @TestInstance annotation above the nested class @TestInstance (TestInstance.Lifecycle.PER_CLASS).

Examples of JUnit 5 Nested tests class

Let’s get into a deeper discussion on JUnit 5 nested tests and check how to use the @Nested annotation in the tests with inner classes.

Before we begin writing the JUnit 5 nested tests and discussing the code, let me tell you about the tools and the website under test.

The following tools are used in writing and running the tests:

  • Programming Language: Java.
  • Web Automation framework: Selenium WebDriver (version 4.5.3).
  • Test Runner: JUnit 5.

Test Scenario.

LambdaTest E-Commerce Playground Website is used for writing the demo tests for this blog on JUnit 5 nested tests. This website is used as it has a complete end-to-end demo business flow from registering a user to adding and checking out the product and confirming the order.

Data driven testing framework

Multiple screens have various fields like textboxes, checkboxes, radio buttons, dropdown fields, etc., which can be used to demonstrate good examples of writing automated tests using Selenium WebDriver.

It also gives newbies in automation testing a fair idea of performing testing using Selenium WebDriver and practicing writing automated tests. As this is a demo website, anyone can try their hands on this website and learn automation testing.

We would use the following strategy to test the registration and log in flow:

  • Register a new User and check if registration is successful.
  • Login to the application using an Invalid Username and check that the appropriate message is displayed.
  • Login into the application using an Invalid Password and check that the appropriate message is displayed.
  • Login into the application using Blank Username and Password and check that the appropriate message is displayed.
  • Login to the application using a Valid Username and Valid Password and check if the login is successful.

GitHub Repository.

All of the code showcased above is located in this GitHub Repository.

Project Structure.

As we have used Maven as a build tool for this project, here is what the project structure looks like:

Nested Tests In JUnit 5 project structure

Now, let’s get into the deeper discussion and start learning about writing tests and using the @Nested annotation.

Before we begin writing the tests, let's add the dependency for JUnit 5 and Selenium WebDriver in the pom.xml. These dependencies will help us use the JUnit 5 framework and Selenium WebDriver classes and methods in our tests.

pom.xml
.

<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>io.github.mfaisalkhatri</groupId>
   <artifactId>junit-examples</artifactId>
   <version>1.0-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>junit-examples</name>
   <url>http://maven.apache.org</url>

   <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <maven.compiler.source>11</maven.compiler.source>
       <maven.compiler.target>11</maven.compiler.target>
       <junit.version>5.9.1</junit.version>
       <selenium.version>4.5.3</selenium.version>
       <log4jcore.version>2.19.0</log4jcore.version>
       <log4japi.version>2.19.0</log4japi.version>
       <javafaker.version>1.0.2</javafaker.version>
       <lombok.version>1.18.24</lombok.version>
       <maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
   </properties>

   <dependencies>
       <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
       <dependency>
           <groupId>org.junit.jupiter</groupId>
           <artifactId>junit-jupiter</artifactId>
           <version>${junit.version}</version>
           <scope>test</scope>
       </dependency>
       <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
       <dependency>
           <groupId>org.seleniumhq.selenium</groupId>
           <artifactId>selenium-java</artifactId>
           <version>${selenium.version}</version>
       </dependency>
       <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
       <dependency>
           <groupId>org.apache.logging.log4j</groupId>
           <artifactId>log4j-core</artifactId>
           <version>${log4jcore.version}</version>
       </dependency>
       <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
       <dependency>
           <groupId>org.apache.logging.log4j</groupId>
           <artifactId>log4j-api</artifactId>
           <version>${log4japi.version}</version>
       </dependency>
       <!-- https://mvnrepository.com/artifact/com.github.javafaker/javafaker -->
       <dependency>
           <groupId>com.github.javafaker</groupId>
           <artifactId>javafaker</artifactId>
           <version>${javafaker.version}</version>
       </dependency>
       <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>${lombok.version}</version>
           <scope>provided</scope>
       </dependency>
   </dependencies>

   <build>
       <plugins>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-surefire-plugin</artifactId>
               <version>${maven-surefire-plugin.version}</version>
               <configuration>
                   <includes>**/*Test*.java</includes>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>

The first and most important task is to set up the drivers so we could use them to run the test. Here, `RemoteWebDriver` is used to run the tests, as we would be running the tests on the LambdaTest platform.

Cloud-based testing platform like LambdaTest allow you to perform cross browser testing over an online browser farm of 3000+ browser and browser versions. You can expand your browser coverage by running your Selenium JUnit test scripts across numerous desktop and mobile environments and ensure a seamless user experience across all devices and OS combinations.

Also, subscribe to LambdaTest YouTube Channel and get detailed tutorials around Selenium automation, Cypress testing, and more.

Here is the code snippet from the DriverManager class where we set the driver and capabilities, so our tests run successfully on the LambdaTest platform.

Configuration (FileName - DriverManager)

public class DriverManager {
   private static final ThreadLocal<WebDriver> DRIVER          = new ThreadLocal<> ();
   private static final Logger                 LOG             = LogManager.getLogger ("DriverManager.class");
   private static final String                 GRID_URL        = "@hub.lambdatest.com/wd/hub";
   private static final String                 LT_ACCESS_TOKEN = System.getProperty ("accessKey");
   private static final String                 LT_USERNAME     = System.getProperty ("username");
   public static void createDriver () {
       setupChromeInLambdaTest ();
       setupBrowserTimeouts ();
   }
   public static <D extends WebDriver> D getDriver () {
       return (D) DriverManager.DRIVER.get ();
   }
   public static void quitDriver () {
       if (null != DRIVER.get ()) {
           LOG.info ("Closing the driver...");
           getDriver ().quit ();
           DRIVER.remove ();
       }
   }
   private static void setDriver (final WebDriver driver) {
       DriverManager.DRIVER.set (driver);
   }
   private static void setupBrowserTimeouts () {
       LOG.info ("Setting Browser Timeouts....");
       getDriver ().manage ()
           .timeouts ()
           .implicitlyWait (Duration.ofSeconds (30));
       getDriver ().manage ()
           .timeouts ()
           .pageLoadTimeout (Duration.ofSeconds (30));
       getDriver ().manage ()
           .timeouts ()
           .scriptTimeout (Duration.ofSeconds (30));
   }
   private static void setupChromeInLambdaTest () {
       final ChromeOptions browserOptions = new ChromeOptions ();
       browserOptions.setPlatformName ("Windows 10");
       browserOptions.setBrowserVersion ("107.0");
       final HashMap<String, Object> ltOptions = new HashMap<> ();
       ltOptions.put ("username", LT_USERNAME);
       ltOptions.put ("accessKey", LT_ACCESS_TOKEN);
       ltOptions.put ("resolution", "2560x1440");
       ltOptions.put ("selenium_version", "4.0.0");
       ltOptions.put ("build", "LambdaTest ECommerce Playground Build");
       ltOptions.put ("name", "Tests for JUnit 5 Tutorial");
       ltOptions.put ("w3c", true);
       ltOptions.put ("plugin", "java-junit");
       browserOptions.setCapability ("LT:Options", ltOptions);
       try {
           setDriver (
               new RemoteWebDriver (new URL (format ("https://{0}:{1}{2}", LT_USERNAME, LT_ACCESS_TOKEN, GRID_URL)),
                   browserOptions));
       } catch (final MalformedURLException e) {
           LOG.error ("Error setting up chrome browser in LambdaTest", e.getMessage ());
       }
   }
   private DriverManager () {
   }
}

GitHub

The following are prerequisite to run the tests on LambdaTest platform:

Field NameDescription
LambdaTest UsernameThis value can be found in the Profile Section of the LambdaTest website after registration.
LambdaTest access tokenThis value can be found in the Profile Section of the LambdaTest website after registration.
GRID URLThis URL is compulsorily required to run the tests on the LambdaTest platform. Value is: “@hub.lambdatest.com/wd/hub”
Other important capabilitiesThese are capabilities like Platform name, browser version, Selenium version, plugin for java-junit, resolution, etc., required to run the tests.

Username, access token, and grid URL are defined as constant (static final) values as these are not going to change anytime throughout the tests.

Capabilities specific to Selenium WebDriver (Mandatory for running the tests)

Capability NameDescriptionValue to be set
Platform NameFor setting the OS name.OS Name like Windows 10/ Windows 11, etc.
Browser VersionFor setting the BrowserVersion.Browser version.

Let’s now begin with writing the tests.

Here is how the test class looks like:

LambdaTestEcommerceWebsiteTest.java

public class LambdaTestEcommerceWebsiteTest {
private static String registeredEmail = null;
private static String password        = null;
@BeforeAll
public static void testSetup () {
    createDriver ();
    final String website = "https://ecommerce-playground.lambdatest.io/";
    getDriver ().get (website);
}
@Test
@DisplayName ("Test by registering a new user")
public void testRegisterUser () {
    HomePage homePage = new HomePage ();
    RegistrationPage registrationPage = homePage.openUserRegistrationPage ();
    registeredEmail = registrationPage.getRegisteredEmail ();
    password = registrationPage.getPassword ();
    assertEquals ("Register Account", registrationPage.getPageHeader ());
    RegistrationSuccessPage registrationSuccessPage = registrationPage.registerUser ();
    assertEquals ("Your Account Has Been Created!", registrationSuccessPage.getRegistrationSuccessMessage ());
    AccountPage accountPage = new AccountPage ();
    accountPage.logoutLink ()
        .click ();
}
@Nested
@DisplayName ("Login Functionality Tests")
@TestMethodOrder (MethodOrderer.OrderAnnotation.class)
class Login {
    private LoginPage loginPage;
    @BeforeEach
    public void testSetup () {
        final String website = "https://ecommerce-playground.lambdatest.io/";
        getDriver ().get (website);
        HomePage homePage = new HomePage ();
        loginPage = homePage.openLoginPage ();
    }
    @Test
    @DisplayName ("Should not allow logging in user with Invalid Username")
    @Order(1)
    void testLoginWithInvalidUsername () {
        loginPage.loginIntoWebsite ("test_invalid_username@gmail.com", password);
        assertEquals ("Warning: No match for E-Mail Address and/or Password.", loginPage.getFailedLoginWarning ());
    }
    @Test
    @DisplayName ("Should not allow logging in user with Invalid Password")
    @Order(2)
    void testLoginWithInvalidPassword () {
        loginPage.loginIntoWebsite (registeredEmail, "InvalidPassword");
        assertEquals ("Warning: No match for E-Mail Address and/or Password.", loginPage.getFailedLoginWarning ());
    }
    @Test
    @DisplayName ("Should not allow logging in user by keeping Username and Password fields blank")
    @Order(3)
    void testLoginWithBlankUsernameAndPassword () {
        loginPage.loginIntoWebsite (" ", " ");
        assertEquals ("Warning: No match for E-Mail Address and/or Password.", loginPage.getFailedLoginWarning ());
    }
    @Test
    @DisplayName ("Should allow logging in with Valid credentials")
    @Order(4)
    void testLoginWithValidCredentials () {
        loginPage.loginIntoWebsite (registeredEmail, password);
        AccountPage accountPage = new AccountPage ();
        assertTrue (accountPage.logoutLink ()
            .isDisplayed ());
    }
}
@AfterAll
public static void tearDown () {
    quitDriver ();
}
}

Test Scenario 1: User Registration

We would be writing this test in the outermost class and using this registered user in further login tests to login into the application.

Here is the User registration screen, which is under test:

user registration screen lambdatest

Before we run the tests, we need to set up the test base as it is a one-time requirement that will be used for further tests in the test class, and we will be doing this by using JUnit’s @BeforeAll annotation.


@BeforeAll
public static void testSetup () {
   createDriver ();
   final String website = "https://ecommerce-playground.lambdatest.io/";
   getDriver ().get (website);
}

Before we run any tests, we would be creating the Driver(Starting the Chrome browser on LambdaTest platform) and navigating to the LambdaTest ECommerce Playground website. Next, we would be testing by registering a new user.

Here is the register user test:


@Test
@DisplayName ("Test by registering a new user")
public void testRegisterUser () {
   HomePage homePage = new HomePage ();
   RegistrationPage registrationPage = homePage.openUserRegistrationPage ();
   registeredEmail = registrationPage.getRegisteredEmail ();
   password = registrationPage.getPassword ();
   assertEquals ("Register Account", registrationPage.getPageHeader ());
  
   RegistrationSuccessPage registrationSuccessPage = registrationPage.registerUser ();
   assertEquals ("Your Account Has Been Created!", registrationSuccessPage.getRegistrationSuccessMessage ());
   AccountPage accountPage = new AccountPage ();
   accountPage.logoutLink ()
       .click ();
}

Our testSetup() method, which runs before any test, would navigate us to the Homepage of the application. We will have to navigate to the Register User Page, under the My Account menu on Home Page.

my account menu on home page

Notice the registeredEmail and password variables in the test, it is created to store the values of the email and password that would be passed when registering the user as these values would be required for a valid login test.

The Register User Test code is simple to understand. It performs the following steps:

  • Navigates the user to the Registration Page.
  • Check the Registration Page header. This is done to ensure we land on the correct page before we start the registration process.
  • Next, it will register the user and verify the registration success message.
  • After which, it will click on the Logout link to log out the user. This marks the completion of our Registration tests.

The register user test is included in the Outer class, i.e., in the main test class.

Now let's move towards our next tests, where we need to cover the login functionality.

Test Scenario 2: User Login

As you know, we would need to cover multiple scenarios as a part of the login functionality test, where we need to perform negative testing to check that the login feature is working perfectly fine, as expected.

We will cover the following scenarios as part of the login feature test and using @Nested annotation.

  • Login to the application using an Invalid Username and check that the appropriate message is displayed.
  • Login into the application using an Invalid Password and check that the appropriate message is displayed.
  • Login into the application using Blank Username and Password and check that the appropriate message is displayed.
  • Login to the application using a Valid Username and Valid Password and check if the login is successful.

Let’s go step by step into the discussion:


@Nested
@DisplayName ("Login Functionality Tests")
@TestMethodOrder (MethodOrderer.OrderAnnotation.class)
class Login {

   private LoginPage loginPage;

   @BeforeEach
   public void testSetup () {
       final String website = "https://ecommerce-playground.lambdatest.io/";
       getDriver ().get (website);
       HomePage homePage = new HomePage ();
       loginPage = homePage.openLoginPage ();
   }

We already discussed the rule for using @Nested annotation, and we will follow it for writing our tests. The Inner class is created, and the @Nested tag is placed on top. Next, the @DisplayName tag is used. This tag helps us give meaningful names to our tests instead of showing the class/method name once the tests are run.

@BeforeEach annotation can be used only once in the test life cycle, and it will run the method on which it is placed every time before running any tests within the class. We have used it to ensure that the user is on the Login Page before we run our login tests.

testSetup() method will navigate to the website and open the Login Page so our tests can run successfully. It makes sense to write these setup steps only once using @BeforeEach. Otherwise, we would have to copy and paste duplicate code to open the Login page every time inside the tests before we perform the login.


@Test
@DisplayName ("Should not allow logging in user with Invalid Username")
@Order(1)
void testLoginWithInvalidUsername () {
   loginPage.loginIntoWebsite ("test_invalid_username@gmail.com", password);
   assertEquals ("Warning: No match for E-Mail Address and/or Password.", loginPage.getFailedLoginWarning ());
}

This test will check that the system should not allow the user to login with Invalid Username and will check for the failed warning message once the user clicks on the login button.


@Test
@DisplayName ("Should not allow logging in user with Invalid Password")
@Order(2)
void testLoginWithInvalidPassword () {
   loginPage.loginIntoWebsite (registeredEmail, "InvalidPassword");
   assertEquals ("Warning: No match for E-Mail Address and/or Password.", loginPage.getFailedLoginWarning ());

}

This test will check that the system should not allow the user to login with Invalid Password and will check for the failed warning message once the user clicks on the login button.


@Test
@DisplayName ("Should not allow logging in user by keeping Username and Password fields blank")
@Order(3)
void testLoginWithBlankUsernameAndPassword () {
   loginPage.loginIntoWebsite (" ", " ");
   assertEquals ("Warning: No match for E-Mail Address and/or Password.", loginPage.getFailedLoginWarning ());

}

This test will check that the system should not allow the user to login by keeping Username as well as Password blank and will check for the failed warning message once the user clicks on the login button.


@Test
@DisplayName ("Should allow logging in with Valid credentials")
@Order(4)
void testLoginWithValidCredentials () {
   loginPage.loginIntoWebsite (registeredEmail, password);
   AccountPage accountPage = new AccountPage ();
   assertTrue (accountPage.logoutLink ()
       .isDisplayed ());
}

Finally, we would check the happy path scenario where the user enters the registered email id and valid password and login into the website. Once login is successful, the user will be navigated to the Account Page, where he will see the logout link, and once this validation passes, it marks the end of our Login functionality tests.

We have covered all four scenarios here. However, we need to order them so that all the negative tests run first and valid login tests run in the end.

For this, we would use @Order annotation above the respective tests. To make the @Order annotation work, we must pass the @TestMethodOrder (MethodOrderer.OrderAnnotation.class) above the nested class name.

Once all the tests are run, we have the @AfterAll annotation placed over the tearDown() method in the main test class, which will help in gracefully quitting the browser and closing the session.


@AfterAll
public static void tearDown () {
   quitDriver ();
}

Our tests would be running on the LambdaTest platform on Windows 10 OS and Chrome browser.

Let’s run our tests now and check the results for JUnit 5 nested tests

Test Execution

There are two ways to run the JUnit 5 nested tests, and in both ways, tests would be running on the LambdaTest cloud platform on the Chrome Browser.

  • From the IntelliJ IDE using JUnit.
  • From the CLI using Maven.

JUnit 5

  • In IntelliJ IDE, open the Run Configuration menu using the Run >> Edit configurations >> JUnit menu and add the LambdaTest username and access key in the VM option as follows:
    • Dusername=<LambdaTest username>
    • DaccessKey=<LambdaTest access key>
  • Click on Apply and OK to start running the tests.
running the tests again

Following is the screenshot from IntelliJ, which shows the execution status of the JUnit 5 nested tests:

LambdaTest ecomerce website page lookout

Check out how @Nested annotation helps group the JUnit 5 nested tests. Login Functionality tests are the display name we gave to our Nested Class, which has all four tests we ran for the login scenario. It gives us a fair idea about the JUnit 5 nested tests run for the Login feature and also shows the respective test result for each test by marking them with a green tick meaning it passed.

Maven

To run the JUnit 5 nested tests using Maven, the following steps need to be run:

  • Open command Prompt/Terminal.
  • Navigate to the root folder of the project.
  • Type the command: mvn test -Dusername=<LambdaTest username> -DaccessKey=<LambdaTest accessKey> .

Following is the screenshot from IntelliJ, which shows the execution status of the tests using Maven:

screenshot from intellij

Once the JUnit 5 nested tests are run successfully, we can check out the LambdaTest Dashboard and view all the video recordings, screenshots, device logs, and step-by-step granular details of the test run.

Check out the Screenshots below, which will give you a fair idea of what the dashboard for automated tests looks like.

Lambdatest ecommerce playground build

The following Screenshots show the details of the build and the JUnit 5 nested tests that were run. Again, the test name, browser name, browser version, OS name, respective OS version, and screen resolution are all correctly visible for each test.

It also has the video of the test that was run, giving a better idea about how JUnit 5 nested tests were run in the browser.

junit nested tests were run in the browser

This screen shows all the metrics in detail, which are very helpful from the tester’s point of view to check what test was run on which browser and, accordingly, also view the Selenium logs.

view the selenium logs

If you're a tester or developer looking to enhance your Selenium JUnit skills, this LambdaTest certification course will help you get started.

LambdaTest

Conclusion

In this blog on JUnit 5 nested tests, we discussed the JUnit 5 feature - @Nested annotation, which allows creating an inner class within the main test class to break the complex logic and group all the tests using the inner class. We also saw how to run the JUnit 5 nested tests using Maven and IntelliJ. We also took a quick view and understanding of how LambdaTest Platform helps run web automation tests and displays the detailed report of the test execution.

Happy Testing!

Frequently Asked Questions (FAQs)

What is a nested test in JUnit?

Nested tests are a feature of JUnit that allows you to set up multiple test cases in the same test class. The main purpose is to reuse the common code between all the different test cases. Within a single test class, you can have as many nested classes as needed (up to the limit imposed by your IDE).

What are the four types of nested classes?

You can nest the four main types of classes within each other. Nested Inner Class, Method Local Inner Classes, Static Nested Classes and Anonymous Inner Classes

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Did you find this page helpful?

Helpful

NotHelpful