In this section, we will delve into advanced assertions in JUnit. These assertions go beyond the basic checks and allow for more complex and detailed testing scenarios. Understanding and utilizing these advanced assertions will enable you to write more robust and comprehensive tests.

Key Concepts

  1. Assertions with Messages: Adding custom messages to assertions for better debugging.
  2. Grouped Assertions: Using assertAll to group multiple assertions together.
  3. Exception Assertions: Asserting that a specific exception is thrown.
  4. Timeout Assertions: Ensuring that a piece of code completes within a specified time.
  5. Custom Assertions: Creating your own assertion methods for reusable and readable tests.

Assertions with Messages

Adding custom messages to assertions can help you understand why a test failed. This is particularly useful when dealing with complex test cases.

Example

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

public class AdvancedAssertionsTest {

    @Test
    void testWithCustomMessage() {
        int expected = 5;
        int actual = 3;
        assertEquals(expected, actual, "The actual value did not match the expected value");
    }
}

In this example, if the test fails, the message "The actual value did not match the expected value" will be displayed, providing more context for the failure.

Grouped Assertions

JUnit 5 allows you to group multiple assertions together using assertAll. This is useful when you want to check multiple conditions in a single test.

Example

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

public class AdvancedAssertionsTest {

    @Test
    void testGroupedAssertions() {
        assertAll("Grouped Assertions",
            () -> assertEquals(4, 2 + 2),
            () -> assertTrue("Hello".startsWith("H")),
            () -> assertFalse("World".isEmpty())
        );
    }
}

In this example, all assertions within assertAll are executed, and the test will report all failures, not just the first one.

Exception Assertions

You can assert that a specific exception is thrown using assertThrows.

Example

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

public class AdvancedAssertionsTest {

    @Test
    void testException() {
        Exception exception = assertThrows(ArithmeticException.class, () -> {
            int result = 1 / 0;
        });
        assertEquals("/ by zero", exception.getMessage());
    }
}

In this example, the test checks that an ArithmeticException is thrown and verifies the exception message.

Timeout Assertions

JUnit 5 provides assertTimeout and assertTimeoutPreemptively to ensure that a piece of code completes within a specified time.

Example

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import java.time.Duration;

public class AdvancedAssertionsTest {

    @Test
    void testTimeout() {
        assertTimeout(Duration.ofSeconds(1), () -> {
            // Simulate task that takes less than 1 second
            Thread.sleep(500);
        });
    }
}

In this example, the test will fail if the code inside the lambda expression takes more than 1 second to execute.

Custom Assertions

Creating custom assertions can make your tests more readable and reusable. You can encapsulate common checks into methods.

Example

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

public class AdvancedAssertionsTest {

    @Test
    void testCustomAssertion() {
        Person person = new Person("John", 25);
        assertPersonIsAdult(person);
    }

    void assertPersonIsAdult(Person person) {
        assertTrue(person.getAge() >= 18, "Person should be an adult");
    }

    class Person {
        private String name;
        private int age;

        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        int getAge() {
            return age;
        }
    }
}

In this example, assertPersonIsAdult is a custom assertion method that checks if a person is an adult.

Practical Exercise

Exercise

  1. Create a test class AdvancedAssertionsExerciseTest.
  2. Write a test method testAdvancedAssertions that:
    • Uses assertAll to group multiple assertions.
    • Asserts that a NullPointerException is thrown when accessing a method on a null object.
    • Uses assertTimeout to ensure a method completes within 2 seconds.
    • Includes a custom assertion method to check if a string is a valid email.

Solution

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import java.time.Duration;

public class AdvancedAssertionsExerciseTest {

    @Test
    void testAdvancedAssertions() {
        assertAll("Grouped Assertions",
            () -> assertEquals(10, 5 + 5),
            () -> assertTrue("JUnit".contains("Unit")),
            () -> assertFalse("Test".isEmpty())
        );

        assertThrows(NullPointerException.class, () -> {
            String str = null;
            str.length();
        });

        assertTimeout(Duration.ofSeconds(2), () -> {
            // Simulate task that takes less than 2 seconds
            Thread.sleep(1000);
        });

        assertValidEmail("[email protected]");
    }

    void assertValidEmail(String email) {
        assertTrue(email.contains("@") && email.contains("."), "Email should be valid");
    }
}

Conclusion

In this section, we explored advanced assertions in JUnit, including assertions with messages, grouped assertions, exception assertions, timeout assertions, and custom assertions. These advanced techniques will help you write more comprehensive and maintainable tests. In the next module, we will dive into parameterized tests, which allow you to run the same test with different inputs.

© Copyright 2024. All rights reserved