Welcome to "Coding Center"!! Coding Center is the place where you will find something new to learn about Java and Python. If you are looking to brush up on your skills before going for an interview, this article is for you...

JUnit 5 Tutorial For Absolute Beginners


In this blog, we are going to talk about JUnit 5. JUnit has always been one of the most popular unit-testing frameworks when working on Java applications.

Note- JUnit 5 requires that you have at least Java 8 version installed.


Setting Up JUnit 5

It's quite simple to set up JUnit 5, if you are familiar with Maven then we just need to add the below dependency to our pom.xml file.

<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.9.0</version>
    <scope>test</scope>
</dependency>

Where an IDE lacks support for JUnit 5, we can use maven surefire provider which is a reliable source to run the unit tests (if IDE has support then this point is not required)

<plugin>
     <artifactId>maven-surefire-plugin</artifactId>
     <version>2.19.1</version>
     <dependencies>
          <dependency>
               <groupId>org.junit.platform</groupId>
               <artifactId>junit-platform-surefire-provider</artifactId>
               <version>1.3.2</version>
          </dependency>
     </dependencies>
</plugin>


JUnit 5 Architecture

JUnit 5 architecture comprises 3 components - JUnit Platform, JUnit Jupiter, and JUnit Vintage. Now, let's understand what they are actually.



1. JUnit Platform

Testing frameworks are launched on the JVM by the platform. It specifies a reliable and potent interface for JUnit's users, such as build tools. To find and run tests, the platform easily connects clients with JUnit.

Additionally, it specifies the TestEngine API for creating testing frameworks that utilize the JUnit software. We can easily integrate third-party testing libraries into JUnit by developing a new TestEngine.


2. JUnit Jupiter

JUnit Jupiter provides a blend of the new programming models for writing unit tests and models for extensions. It also introduced a lot of additional new annotations like @BeforeEach, @AfterEach, @AfterAll, @BeforeAll, etc.

3. JUnit Vintage

JUnit Vintage supports executing previous JUnit version 3 and 4 tests on this new platform.


Basic Annotations


@BeforeAll

This annotation will help you execute a logic once before all the test cases get executed within a class. One important thing to note here is that the method with @BeforeAll annotation needs to be static. Now, let's see an example.

public class BeforeAndAfterAnnotationExample {

    @BeforeAll
    static void beforeAll() {
        System.out.println("Executed once before all test cases in this class");
    }

}

@AfterAll

This annotation is just like @BeforeAll annotation and the only difference is that it will help you execute a logic once after(instead of before in @BeforeAll) all the test cases get executed within a class. Now, let's see an example.

public class BeforeAndAfterAnnotationExample {

    @AfterAll
    static void afterAll() {
        System.out.println("Executed after all test cases in this class");
    }

}


@BeforeEach

This annotation will help you execute a logic once before each test case gets executed within a class. Now, let's see an example.

public class BeforeAndAfterAnnotationExample {

    @BeforeEach
    static void beforeEach() {
        System.out.println("Executed before each test cases in this class");
    }

}

@AfterEach

This annotation is just like @BeforeEach annotation and the only difference is that it will help you execute a logic once after(instead of before in @BeforeEach) each of the test cases gets executed within a class. Now, let's see an example.

public class BeforeAndAfterAnnotationExample {

    @AfterEach
    static void afterEach() {
        System.out.println("Executed after each test cases in this class");
    }

}


@DisplayName

This annotation is used to define the name of the test which is displayed to the user. Now, let's take an example.

public class DisplayNameAnnotationExample {

    @Test
    @DisplayName("This is Test 1")
    void test_1() {
        System.out.println("Executing test 1");
    }

}

Output-

@Disabled

This annotation is used to disable a particular test method or test method or an entire test class. Now, let's see an example.

@Disabled on test method
public class DisabledAnnotationExample {

    @Test
    @Disabled
    void test_dummy() {
        System.out.println("This is a dummy test");
    }

}

Output


@Disabled on the entire test class
This will disable the entire test class which means all the test methods in the test class will get disabled.

@Disabled
public class DisabledAnnotationExample {

    @Test
    void test_dummy() {
        System.out.println("This is a dummy test");
    }

}

Output

Assertions

Assertions were already there before JUnit 5. Assertions methods are all static and each method is used to see if the condition/result is true or not. With JUnit 5, we can also use Lambda expressions with Assertions, and now Assertions have been moved to package org.junit.jupiter.api.

Now, let's take an example. Here, as you can see the sum of all the numbers on the list equates to 7 that's why the output will be a success.

@Test
void test_assertion() {
    List<Integer> nums = Arrays.asList(1,2,4);

    Assertions.assertEquals(7, nums.stream().mapToInt(Integer::intValue).sum(), "Sum is not equal to 7");
}


Output

But, if the sum of all the numbers on the list doesn't equate to 7 then the test case will fail with the error message "Sum is not equal to 7".



Assumptions

In the class org.junit.jupiter.api.Assumptions, assumptions are static methods. They will only run a test if the required condition is satisfied; else, the test will be aborted. The failed test won't result in the build failing. org.opentest4j.TestAbortedException is thrown and the test is skipped when an assumption is incorrect.

Now, let's take an example of successful scneario.

public class AssumptionsExample {

    @BeforeAll
    static void setup() {
        System.setProperty("APP_MODE", "DEV");
    }

    @Test
    void test_1() {
        Assumptions.assumeTrue("DEV".equals(System.getProperty("APP_MODE")), "Aborting as the mode is not DEV");
        Assertions.assertEquals(3, 1 + 2);
    }

    @Test
    void test_2() {
        Assumptions.assumingThat("DEV".equals(System.getProperty("APP_MODE")), () -> {
            Assertions.assertEquals(3, 1 + 2, "Sum is not as expected");
        });
    }

}

For this scenario, the output will be-

Now, let's take an example of unsuccessful scenario. Here, I have changed the value of property "APP_MODE" to "DEV1".

public class AssumptionsExample {

    @BeforeAll
    static void setup() {
        System.setProperty("APP_MODE", "DEV1");
    }

    @Test
    void test_1() {
        Assumptions.assumeTrue("DEV".equals(System.getProperty("APP_MODE")), "Aborting as the mode is not DEV");
        Assertions.assertEquals(3, 1 + 2);
    }

}

For this scenario, the output will be-


Nested Test Classes

Nested tests let you build nested classes and run each of their test methods. Inner classes need not be static. To run all of the test methods inside an inner class, simply annotate it with @Nested. Now, let's take an example.

public class NestedTestExample {

    @BeforeAll
    static void setup() {
        System.setProperty("APP_MODE", "DEV");
    }

    @Nested
    @DisplayName("All DEV env tests")
    class DevEnvTest {

        @Tag("DEV")
        @Test
        @DisplayName("This is Test 1")
        void test_1() {
            Assumptions.assumeTrue("DEV".equals(System.getProperty("APP_MODE")), "Aborting as the mode is not DEV");
            Assertions.assertEquals(3, 1 + 2);
        }

        @Tag("DEV")
        @Test
        @Disabled("Disabled until some requirement has been fulfilled")
        void test_2() {
            Assumptions.assumingThat("DEV".equals(System.getProperty("APP_MODE")), () -> {
                Assertions.assertEquals(3, 1 + 2, "Sum is not as expected");
            });
        }
    }

}

Output-

Exceptions Testing

There are instances where procedures must throw an exception when a particular circumstance exists. If the given method does not throw the desired exception, assertThrows will fail the test.  Now, let's take an example.

public class ExceptionExample {

    @Test
    void test_exception() {
        
        Throwable ex = Assertions.assertThrows(RuntimeException.class, () -> {
            throw new RuntimeException("An Exception Occurred");
        });
        Assertions.assertEquals("An Exception Occurred", ex.getMessage());
    }

}

Output-



No comments:

Post a Comment

| Designed by Colorlib