Spring Boot HandBook

    Introduction :#

    JUnit and AssertJ are both popular libraries in the Java ecosystem for writing and managing unit tests, but they serve different purposes and complement each other well.

    Add dependency#

    For testing, we have already added the spring-boot-starter-test dependency to your application.

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>

    Try pressing Ctrl + click on the dependency to navigate to it. Inside this dependency, you will find many additional dependencies: assertj-core and junit-jupiter and mockito-junit-jupiter.

    JUnit#

    JUnit is a framework for writing and running tests in Java. It provides the basic structure for test cases, including annotations to define test methods and assertions to check expected outcomes.

    Common Junit Annotations

    • @Test
      • Marks a method as a test method.
      • JUnit will execute this method when running tests.
      • Example
    import org.junit.jupiter.api.*; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class DemoApplicationTests { @Test void printHello(){ System.out.println("Hello! World"); } }
    • Output

    If you choose to remove @SpringBootTest, you can do so. However, the output structure will be affected as follows.

     

    • @DisplayName
      • Sets a custom display name for the test class or test method.
      • This name is used in test reports and IDEs to make test results more readable.
      • Example
    import org.junit.jupiter.api.*; class DemoApplicationTests { @Test @DisplayName("XZY") void test(){ System.out.println("XYZ"); } }
    • Output

    • @Disabled
      • Disables a test class or test method, so it is not executed.
      • Useful for skipping tests that are not relevant for a particular run or are known to be failing.
      • Example
    import org.junit.jupiter.api.*; class DemoApplicationTests { @Test void contextLoads() { System.out.println("ABC"); } @Test //this test is disabled @Disabled void test(){ System.out.println("XYZ"); } }
    • Output

    • @BeforeEach
      • Indicates that the annotated method should be executed before each test method.
      • Useful for setting up common test data or state before each test runs.
      • Example
    import org.junit.jupiter.api.*; class DemoApplicationTests { @BeforeEach void runBeforeEachTest(){ System.out.println("Run Before Each Test Cases"); } @Test void testOne() { System.out.println("Test One Case"); } @Test void testTwo() { System.out.println("Test Two Case"); } }
    • Output

    • @AfterEach
      • Indicates that the annotated method should be executed after each test method.
      • Useful for cleaning up resources or resetting states after each test.
      • Example
    import org.junit.jupiter.api.*; class DemoApplicationTests { @Test void testOne() { System.out.println("Test One Case"); } @Test void testTwo() { System.out.println("Test Two Case"); } @AfterEach void runAfterEachTest(){ System.out.println("Run After Each Test"); } }
    • Output

    • @BeforeAll
      • Indicates that the annotated method should be executed once before all test methods in the class.
      • The method must be static.
      • Useful for expensive setup operations that are shared across all tests.
      • Example
    import org.junit.jupiter.api.*; class DemoApplicationTests { @BeforeAll static void runBeforeAllTests(){ //this must be static System.out.println("Run Once Before All Test Cases"); } @Test void testOne() { System.out.println("Test One Case"); } @Test void testTwo() { System.out.println("Test Two Case"); } }
    • Output

    • @AfterAll
      • Indicates that the annotated method should be executed once after all test methods in the class.
      • The method must be static.
      • Useful for cleaning up resources or performing final actions after all tests have run.
      • Example
    import org.junit.jupiter.api.*; class DemoApplicationTests { @Test void testOne() { System.out.println("Test One Case"); } @Test void testTwo() { System.out.println("Test Two Case"); } @AfterAll static void runAfterAllTests(){ //this method must be static System.out.println("Run Once After All Test Cases"); } }
    • Output

    More JUnit Functionalities: Overview (JUnit 5.11.0 API)

    Assertion with JUnit#

    JUnit's basic assertions are essential for verifying test conditions. Here’s a detailed explanation of the ones you mentioned:​

    • assertEquals(expected, actual):
      • Compares the expected value with the actual value.
      • If they are not equal, the test fails.
      • Useful for checking if a method returns the correct result.
      • Here, you can use any type of datatype.
      • Example:
    import org.junit.jupiter.api.*; class DemoApplicationTests { @Test void testOne() { int a = 4; int b = 5; int result = addTwoNumbers(a,b); Assertions.assertEquals(9,result); //4+5=9 System.out.println("TestOne has run successfully"); } int addTwoNumbers(int a, int b){ return a+b; } }
    • Output:

    • assertTrue(condition):
      • Checks if the given condition is true.
      • If it’s false, the test fails.
      • Example
    import org.junit.jupiter.api.*; class DemoApplicationTests { @Test void testIsAdult() { int age = 20; boolean isAdult = age >= 18; Assertions.assertTrue(isAdult); // Passes if age >= 18 System.out.println("TestIsAdult has run successfully"); } }
    • Output

    • assertFalse(condition):
      • Checks if the given condition is false.
      • If it’s true, the test fails.
      • Example
    import org.junit.jupiter.api.*; class DemoApplicationTests { @Test void testIsAdult() { int age = 15; boolean isAdult = age >= 18; Assertions.assertFalse(isAdult); // Passes if age >= 18 System.out.println("TestIsAdult has run successfully"); } }
    • Output

    AssertJ#

    AssertJ is a library that provides a rich and fluent API for writing assertions in tests. It offers more expressive and readable assertions compared to JUnit’s built-in assertions.

    • Numbers:
      • assertThat(5).isEqualTo(5).isNotEqualTo(10).isGreaterThan(4);
      • You can chain multiple assertions for better readability and conciseness when working with numbers.
      • Example
    import org.assertj.core.api.Assertions; import org.assertj.core.data.Offset; import org.junit.jupiter.api.*; class DemoApplicationTests { @Test void testOne(){ int a = 4; int b = 5; int result = addTwoNumbers(a,b); Assertions.assertThat(result) .isEqualTo(9) .isNotEqualTo(20) .isCloseTo(8, Offset.offset(1)) .isGreaterThan(7); System.out.println("TestOne Has Run Successfully"); } int addTwoNumbers(int a, int b){ return a+b; } }
    • Output

    • String:
      • assertThat("hello").startsWith("he").endsWith("lo").contains("ell");
      • AssertJ provides powerful assertions for string operations like matching, checking substrings, and more.
      • Example
    import org.assertj.core.api.Assertions; import org.junit.jupiter.api.*; class DemoApplicationTests { @Test void testOne(){ Assertions.assertThat("Apple") .isEqualTo("Apple") .startsWith("A") .endsWith("le") .contains("le") .hasSize(5); System.out.println("TestOne Has Run Successfully"); } }
    • Output

    • Boolean:
      • assertThat(true).isTrue(); OR assertThat(false).isFalse();
      • AssertJ makes assertions on boolean values more readable and intuitive.
      • Example
    import org.assertj.core.api.Assertions; import org.junit.jupiter.api.*; class DemoApplicationTests { @Test void testOne(){ Assertions.assertThat(isAdult(20)).isTrue(); Assertions.assertThat(isAdult(15)).isFalse(); System.out.println("TestOne Has Run Successfully"); } boolean isAdult(int age){ return age >= 18; } }
    • Output

    • List/Array:
      • assertThat(List.of("apple", "banana")).contains("apple") .doesNotContain("orange").hasSize(2);
      • AssertJ provides a wide range of assertions for collections like lists and arrays.
      • Example
    import org.assertj.core.api.Assertions; import org.junit.jupiter.api.*; import java.util.List; class DemoApplicationTests { @Test void testOne(){ Assertions.assertThat(List.of("Apple","Banana","Mango")) .contains("Mango") .doesNotContain("Orange") .hasSize(3); System.out.println("TestOne Has Run Successfully"); } }
    • Output

    • Exceptions:
      • assertThatThrownBy(() -> { throw new IllegalArgumentException("Invalid argument"); }).isInstanceOf(IllegalArgumentException.class) .hasMessage("Invalid argument") .hasStackTraceContaining("ExampleTest");
      • AssertJ offers a fluent API to check for exceptions thrown by code blocks, including checking the exception type, message, and stack trace details.
      • Example
    import org.assertj.core.api.Assertions; import org.junit.jupiter.api.*; class DemoApplicationTests { @Test void testOne(){ int a = 9; int b = 0; Assertions.assertThatThrownBy(()-> divideTwoNumbers(a,b) ) .isInstanceOf(ArithmeticException.class) //or .isInstanceOf(RuntimeException.class) //or .isInstanceOf(Exception.class) //or .hasMessage("Tried to divide by zero") .hasStackTraceContaining("DemoApplicationTests.testOne"); // Checking stack trace contains method name System.out.println("TestOne Has Run Successfully"); } double divideTwoNumbers(int a, int b){ try{ return a/b; } catch (ArithmeticException e){ System.out.println("Arithmetic exception occur: "+e.getLocalizedMessage()); throw new ArithmeticException("Tried to divide by zero"); } } }
    • Output

    More AssertJ Methods: AssertJ - fluent assertions java library

    JUnit vs AssertJ#

    JUnit is one of the most widely used testing frameworks in the Java ecosystem. JUnit provides a simple and standardized way to write test cases, execute them, and report the results. AssertJ, on the other hand, is not a testing framework but rather a library that complements testing frameworks like JUnit. It focuses on providing fluent and expressive assertions, enhancing the readability and maintainability of your test code.

    JUnit and AssertJ are both popular tools used in Java for testing, but they serve different purposes and have distinct features.

    Here, we have already covered these aspects, which are highlighted in green in the image.​

    Last updated on Dec 09, 2024