Nested Class is Placed inside Another Class and is Arranged in a Hierarchical Structure. Learn about Rules, Template & Examples of JUnit 5 Nested Class:
We learned about repeating tests with the same data using the annotation @RepeatedTest in our previous tutorial. We explored the various ways for implementation of @RepeatedTest in a class.
Besides, we also learned about the three interfaces i.e. TestInfo, RepetitionInfo, RepeatedTest, and its implementation in the code.
In this tutorial, we will learn about a feature called Nested class that is supported only by JUnit 5. This is not available in JUnit 4.
=> Take A Look At The JUnit Beginners Guide Here.
Table of Contents:
JUnit 5 Nested Class
In this section, let’s understand the meaning of the term ‘Nested class’ in detail.
Nested class as the name suggests is the class that is placed inside another class and is arranged in a hierarchical structure.
We already know how a Single class within a JUnit class is implemented. Let’s deep-dive into why do we need Nested class within a JUnit class.
When do we need Nested class usage within a JUnit class?
There could be situations when a test class would contain several tests that include complex logic. In such cases, Nested class or classes may be used. Remember that this is not available with JUnit 4.
How do we implement the Nested class?
The high-level answer to this is that the complex and multiple test cases can be broken down in such a way that each test could be smaller and modularized. The related tests can be grouped under each separate class embedded or nested under the main group.
Template Of A JUnit Nested Class?
Here, let’s quickly have a look at how a JUnit class with Nested class looks like. It won’t include the actual code, however, it would put in forth the template of the Nested class to conceptually understand its implementation.
Let’s consider an example of the JUnit class for the Facebook site that includes multiple pages like:
- Creating a new profile,
- Log in for the existing user,
- Which further also validates the username and password field,
- Adding a new post
You are aware of an approach where you have a single class assembling all the test methods. An alternative and a better approach to this would be to create a nested class inside the main class and grouping together the related tests.
Pseudocode: The pseudo-code would look as shown below.
public class FacebookApp{ @BeforeAll static void launchFB(){ //the code to launch FB goes here } @Test @DisplayName(“Testcase to create new user”) void signUp(){ //the code to create new user goes here } @Test @DisplayName(“Testcase to add new post”) void addPost(){ //the code to add new post } @Nested @DisplayName(value=“Nested class for Login to FB”) class login { void verifyUserNameIsNotNull() {//the code to validate if username is blank goes here } void verifyPasswordIsNotNull(){ //code to validate if password is blank goes here } void validLoginCredentials(){ //code to login to FB with valid username and password } } } @AfterAll static void closeFB(){ //the code to close the browser with FB app and //releasing all used objects goes here } }
This was an abstract idea of how a nested class would be implemented.
Why Do We Use The Nested Class?
The Nested class is used by the developers for the following benefits:
- To make a large and complex test class more readable.
- The result looks even better than the one with the usual single class.
Rules For @Nested Annotation
In this section, we will learn certain rules associated with the implementation of the Nested class.
- @Nested annotation is used on a class and not on a specific test.
- 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.
- The nested class has to be non-static i.e. none of the methods within the Nested class can be static.
- In other words, the outermost class can have static methods with annotations @BeforeAll and @AfterAll however, the nested inner classes cannot have static methods for @BeforeAll and @AfterAll annotations.
- Nested classes can have a separate lifecycle than the outer class.
Examples Of JUnit 5 Nested Class
Let’s have a look at a JUnit class that uses the Nested class. Given below is a basic example of the same.
Example #1:
This is example #1 that has verifyLoginID() test method in the outermost class and a nested class with the test method verifyPassword()
The FBLoginClass includes test method verifyLoginID() that verifies if the actual login id and the expected match or not. Similarly, the nested class – nestedPasswordClass includes the test verifyPassword() that verifies if the actual password matches with the expected value.
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; public class FBloginClass{ @Test void verifyLoginID(String id){ id=”user1”; assertEquals(id, “user1”, “Valid login id ”); System.out.println(“OUTERMOST CLASS - FBloginClass”); } @Nested class nestedPasswordClass(){ @Test void verifyPassword(String passwd){ passwd=” mypasswd123”; assertEquals(passwd, “mypasswd123”, “Valid password”); System.out.println(“NESTED CLASS - nestedPasswordClass”); } } }
The resultant after execution is as below:
- The Run count shows 2/2 that includes the test method within the nested class too.
- The outer class runs first and then the nested class runs.
- Here, verifyPassword method is passed hence the root node for its class i.e. nestedPasswordClass shows green check. If verifyPassword fails then the root node shows a red cross.
- Hence, the output is displayed in a very organized way.
The console window is shown as below:
Example #2:
This is example #2 that has verifyLoginID() test method in the outermost class and a nested class with the test method verifyPassword()
The FBLoginClass includes test method verifyLoginID() that verifies if the actual login id and the expected match or not. Similarly, the nested class – nestedPasswordClass includes the test verifyPassword() that verifies if the actual password matches with the expected value.
Another nested class nestedSearchPeopleClass is placed under FBLoginClass with searchPeople() test method.
This is a demonstration where multiple nested classes may exist.
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; public class FBloginClass{ @Test void verifyLoginID(String id){ id=”user1”; assertEquals(id, “user1”, “Valid login id ”); System.out.println(“OUTERMOST CLASS - FBloginClass”); } @Nested class nestedPasswordClass(){ @Test void verifyPassword(String passwd){ passwd=” mypasswd123”; assertEquals(passwd, “mypasswd123”, “Valid password”); System.out.println(“NESTED CLASS - nestedPasswordClass”); } } @Nested class nestedSearchPeopleClass(){ @Test void searchPeople(String personName){ System.out.println(“NESTED CLASS - nestedSearchPeopleClass”); } } }
The result after execution is as shown below:
- The Run count shows 3/3 that includes the test method within the nested classes too.
- The outer class runs first then the nested classes run.
- The method verifyLoginID() under FBLoginClass runs first, then the verifyPassword() under nested class nestedPasswordClass executes and then searchPeople() under nested class nestedSearchPeopleClass executes.
- There is no limit on the depth of the nested class hierarchy.
The console window is as shown below:
Example With Lifecycle Methods From Main Class
We will now demonstrate an example where the nested class doesn’t have its methods annotated with @BeforeAll, @BeforeEach, @AfterEach, and @AfterAll however the main class does have these methods.
public class FBloginClass{ @BeforeAll static void setUp(){ System.out.println(“@BeforeAll- FBloginClass”); } @BeforeEach void beforeEachTest(){ System.out.println(“@BeforeEach- FBloginClass”); } @Test void verifyLoginID(String id){ id=”user1”; assertEquals(id, “user1”, “Valid login id ”); System.out.println(“OUTERMOST CLASS - FBloginClass”); } @Nested class nestedPasswordClass(){ @Test void verifyPassword(String passwd){ passwd=” mypasswd123”; assertEquals(passwd, “mypasswd123”, “Valid password”); System.out.println(“NESTED CLASS - nestedPasswordClass”); } } @AfterEach void afterEachTest(){ System.out.println(“@AfterEach- FBloginClass”); } @AfterAll static void tearDown(){ System.out.println(“@AfterAll-FBloginClass”); } }
Result:
When there are lifecycle call back methods for the outer class only; lifecycle call back methods of the outer class apply to the outerclass tests as well as to the innerclass tests during execution.
Let’s look at the detailed output:
- The method with @BeforeAll executes once before the main class.
- The method with @AfterAll executes once in the end after all the test executes.
- The methods with @BeforeAll and @AfterAll are static methods so by default the nested class doesn’t work with these methods.
- The method with @BeforeEach executes once each i.e. before the main test and the inner test.
- The method with @AfterEach executes once each i.e. after the main test and the inner test.
The console window will be as shown below:
Example With Separate Lifecycle Methods For Nested Class
We will now demonstrate an example where both the main class and the nested class have their lifecycle callback methods. Let’s understand the sequence of the workflow when such a program is executed.
Example #1:
The nested class has its @BeforeEach and @AfterEach methods and outermost class has the methods annotated with @BeforeAll, @AfterAll, @BeforeEach, and @AfterEach.
public class FBloginClass{ @BeforeAll static void setUp(){ System.out.println(“@BeforeAll- FBloginClass”); } @BeforeEach void beforeEachTest(){ System.out.println(“@BeforeEach- FBloginClass”); } @Test void verifyLoginID(String id){ id=”user1”; assertEquals(id, “user1”, “Valid login id ”); System.out.println(“OUTERMOST CLASS - FBloginClass”); } @Nested class nestedPasswordClass(){ @BeforeEach void beforeEachTest_nested(){ System.out.println(“@BeforeEach- nestedPasswordClass”); } @Test void verifyPassword(String passwd){ passwd=” mypasswd123”; assertEquals(passwd, “mypasswd123”, “Valid password”); System.out.println(“NESTED CLASS - nestedPasswordClass”); } @AfterEach void afterEachTest_nested(){ System.out.println(“@AfterEach- nestedPasswordClass”); } }// nested class ends here @AfterEach void afterEachTest(){ System.out.println(“@AfterEach- FBloginClass”); } @AfterAll static void tearDown(){ System.out.println(“@AfterAll-FBloginClass”); } }//main class ends here
Result:
Let’s look at the detailed output:
- The method setUp() annotated with @BeforeAll runs as the first method. However, the nested class does not work with any static methods including the methods annotated with @BeforeAll and @AfterAll under the outer class – FBloginClass.
- Then, the method with beforeEachTest() from the outer class executes.
- The method verifyLoginID() executes next.
- Then, the method afterEachTest() of the outer class executes.
- Later, the method with beforeEachTest() from the outer class executes.
- The method with beforeEachTest_nested() for the nested class executes next. The method annotated with @BeforeEach and @AfterEach from the outer class applies to the nested class too as these are non-static methods. This is why you will see that the beforeEachTest() runs and then beforeEachTest_nested() executes. In short, the methods with the non-static lifecycle callback annotations from both the outer class as well as the nested class apply to the nested class.
- The method verifyPassword() executes next.
- The method with afterEachTest_nested() for nested class executes next.
- Then, the method with afterEachTest() from then the outer class executes.
- The method tearDown() annotated with @AfterAll from the outerclass executes lastly.
The console window is as shown below:
Example #2:
The nested class has its own @BeforeEach and @AfterEach methods, however, it needs to have its own @BeforeClass and @AfterClass as well.
At the same time, we know that the methods annotated with @BeforeClass and @AfterClass are static methods or in other words, shared by the class which is NOT supported by a nested class.
Now, arises the question – how do we then have @BeforeClass and @AfterClass methods under the nested class? The concrete solution to it is an annotation to the class @TestInstance that will help to achieve this.
@TestInstance – Configure The Lifecycle Mode
The class TestInstance which is part of JUnit 5 only helps to configure the lifecycle mode for a specific class. The class TestInstance is imported from the package org.junit.jupiter.api.TestInstance.*;
It includes an enum named “LIFECYCLE”. Thus, TestInstance.LIFECYCLE takes in either of the two modes given below:
#1) PER_METHOD
This is the value that exists implicitly by default. So, when you don’t explicitly add this annotation to a class, the lifecycle mode is by default set as PER_METHOD.
Setting this value creates separate instances of the class for each test method. Thus, unless a method is specified static, all the other non-static methods operate on their separate instances of the class.
#2) PER_CLASS
You may update the configuration by changing the value to PER_CLASS.
Setting this value to a class creates a single instance of the class which is shared across all the test methods within that class. Hence, there is no need to explicitly define a method as static.
This is why the nested class could create non-static methods with @BeforeAll and @AfterAll annotations that still work like static methods.
We hope this detailed information on the annotation @TestInstance might have helped you to resolve this tricky question with such a fascinating solution.
Let’s now move on with the demonstration on the usage of the lifecycle callback methods with @BeforeAll and @AfterAll annotations in the nested class.
@Nested @TestInstance(TestInstance.LIFECYCLE=”PER_CLASS”) class nestedPasswordClass(){ @BeforeAll void setUp_nested(){ System.out.println(“@BeforeAll- nestedPasswordClass”); } @BeforeEach void beforeEachTest_nested(){ System.out.println(“@BeforeEach- nestedPasswordClass”); } @Test void verifyPassword(String passwd){ passwd=” mypasswd123”; assertEquals(passwd, “mypasswd123”, “Valid password”); System.out.println(“NESTED CLASS - nestedPasswordClass”); } @AfterEach void afterEachTest_nested(){ System.out.println(“@AfterEach- nestedPasswordClass”); } @AfterAll void tearDown_nested(){ System.out.println(“@AfterAll- nestedPasswordClass”); } }
Result:
Let’s look at the detailed output:
- The method setUp() is annotated with @BeforeAll runs as the first method.
- Then, the method with beforeEachTest() from the outer class executes.
- The method verifyLoginID() executes next.
- Now, the method afterEachTest() of the outer class executes.
- The method setUp_nested() annotated with @BeforeAll from the nested class executes next.
- Then, the method with beforeEachTest() from the outer class executes.
- The method with beforeEachTest_nested() for nested class executes next.
- The method verifyPassword() executes next.
- The method with afterEachTest_nested() for nested class executes next
- Then, the method with afterEachTest() from the outer class executes.
- The method tearDown_nested() annotated with @AfterAll from the nested class executes next.
- The method tearDown() annotated with @AfterAll from the outerclass executes lastly.
The console window is as shown below:
FAQs On Nested Class
Q #1) How do I access the value of a variable from the outer-most class into the nested class?
Answer: Let’s look at the below code to clarify the answer to this.
public int a, b; public class MathsCalc{ a=10;b=20; @DisplayName=”Multiplication” @Test void multiply(){ assertEquals(200,(a*b), “The value after multiplication is correct”); } @Nested class nestedMathsCalc{ @DisplayName=”Subtraction” @Test void subtract(){ assertEquals(200,(b-a), “The value after subtraction is correct”); } } }
In the above example, by default, the lifecycle mode is PER_METHOD, thus every test method creates a separate instance of its class.
- Having known this, the test method multiply() passes post-execution as both the variables a and b within the method, get their values like 10 and 20, respectively from its class.
- However, the subtract() method in the nested class fails as the method is not able to access the variables from the outermost class, thereby both the variable b and a get null values. Hence, the method fails on the assertion check.
Now, moving to the answer for the base question about how to access a variable value from the outermost class inside the nested class.
- Annotate the outermost class (i.e. MathsCalc class in our example) with @TestInstance(LIFECYCLE=PER_CLASS). The class instance of MathsCalc class is shared by the nestedMathsCalc() class too. This way, the values of the variables will flow from the outer class to the nested class.
- Another alternative is, to use a method with @BeforeEach in the outer class – MathsCalc and assign values as a=10; b=20; in this method. As it is a non-static method from the outer class, during execution, this method will run for the nested class too. At this point, the variable values will be accessed in the nested class from the outer class.
Q #2) Do all the annotations that work for any Test work the same for nested class tests too?
Answer: Yes, all the annotations that work for a method annotated with @Test in a Single class JUnit test works in the same way for a method annotated with @Test in the nested class too.
Q #3) How do I set an execution order for the outer class and the nested class?
Answer: The annotation @TestMethodOrder(OrderAnnotation.class) needs to be used in the class in which you need the ordering of test methods done.
Hence, if you want to order the test methods in the outer class, annotate it with this annotation and then annotate each method in the outer class with @Order(<order number>). Similarly, follow this for the nested class and their methods as well.
Remember, you cannot annotate the nested class with @Order(<index>). The @Order can be applied only to the test methods.
Example:
@TestMethodOrder(OrderAnnotation.class) class MathsClass{ @Order(2) @Test void multiply(){ System.out.println(“Multiply”); } @Order(1) @Test void divide(){ System.out.println(“Divide”); } @Nested @TestMethodOrder(OrderAnnotation.class) class MathsClass_nested{ @Order(2) @Test void add(){ System.out.println(“Add”); } @Order(1) @Test void subtract(){ System.out.println(“Subtract”); }}}
The output on the console window of the above code would be:
The ordering first applies to the test methods from the outer class and then the ordering applies to the test methods from the nested class.
If there are multiple nested classes, despite ordering the test methods, there is no assurance on which the nested class would first run and which would follow next. The order of the execution of the nested classes would be as per the internal algorithm of JUnit.
Conclusion
That’s it with the implementation of the JUnit 5 Nested Class. We explored how the lifecycle call back methods work with Nested class and also learned about interesting details on @TestInstance annotation.
=> Visit Here To Learn JUnit From Scratch.