Open In App

How to Assert Log Messages with JUnit in Java?

Last Updated : 25 Nov, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

In Java applications, logging is essential for monitoring application behavior, debugging issues, and tracking performance. Verifying log messages is important for ensuring correct entries during specific actions. In this article, we will demonstrate how to assert log messages in tests using JUnit.

Prerequisites:

  • Basic knowledge of the Java and JUnit Framework.
  • Maven for building dependency management.
  • Familiarity with logging framework like Logback or Log4j.
  • JDK and IntelliJ IDEA installed in your system.

Logging in Java

Logging is an essential part of software development. It helps monitor application behavior, diagnose problems, and gain insights into application performance. Commonly used logging frameworks in Java include Logback and Log4j.

Logging

Logging refers to the process of recording events, messages, or errors that occur during the execution of a software application. It helps in:

  • Debugging: Identify and fix issues by analyzing logs.
  • Monitoring: Understand how the application behaves over time.
  • Auditing: Maintain a record of important events for security and compliance.

Popular Logging Frameworks in Java

  • Logback: This is a versatile logging framework offering better performance than Log4j. It’s the default choice for many Spring Boot applications.
  • Log4j: This is an older but powerful logging framework that was used widely before Logback.
  • SLF4J (Simple Logging Facade for Java): This is a logging abstraction that allows developers to use different logging frameworks, like Logback or Log4j, through a unified interface, enabling easy configuration and switching between implementations.

Importance of Logging in Testing

In unit tests, logging serves two main purposes:

  • Validation: Ensures that specific actions or error conditions generate the expected log messages.
  • Visibility: Provides insights into the behavior of the application, especially during debugging or test failures.

For example, if an API throws an exception, a unit test can verify that an appropriate error message is logged, ensuring that useful information is recorded for troubleshooting.

Importance of JUnit and Log Assertions

JUnit is a widely used framework for unit testing in Java. While testing functionality, verifying log messages can be as crucial as verifying method outputs. Here's why:

  • Behavior Verification: Some methods may not return values but are expected to log specific information. Verifying logs ensures these methods behave as expected.
  • Error Handling: Checking if the correct error message is logged when exceptions occur ensures the application handles failures appropriately.
  • Code Quality: Log assertions help maintain consistent logging across the codebase, improving traceability and readability of logs.

Project Implementation to Assert Log Messages With JUnit

In this project, we will demonstrate how to assert log messages using JUnit. We will utilize Logback as the logging framework and JUnit 5 for writing test cases in a Maven-based setup.

Step 1: Create a New Maven Project

In IntelliJ IDEA, create a new Maven project with the following options:

  • Name: Log-Assertion-Example
  • Build System: Maven

Click on the Create button.

Project Metadata

Project Structure

Once the project is created, set the folder structure as shown in the below image:

Project Folder Structure

Step 2: Add the Dependencies to pom.xml

Open the pom.xml and add the following JUnit framework and Logging dependencies.

XML
<?xml version="1.0" encoding="UTF-8"?>
<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>com.gfg</groupId>
    <artifactId>Log-Assertion-Example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- JUnit 5 dependency for testing -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.0</version>
            <scope>test</scope>
        </dependency>

        <!-- Logback dependency for logging -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.5.8</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.16</version>
            <scope>test</scope>
        </dependency>

        <!-- SLF4J API -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.9</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Plugin to use Java 17 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>

Step 3: Create CalculatorService.java

Create the CalculatorService class and this class contains the methods that performs the arithmetic operations and log messages using the SLF4J.

Java
package com.gfg;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CalculatorService {
    private static final Logger logger = LoggerFactory.getLogger(CalculatorService.class);

    public int add(int a, int b) 
    {
        logger.info("Adding {} and {}", a, b);
        return a + b;
    }

    public int divide(int a, int b) 
    {
        if (b == 0) {
            logger.error("Division by zero is not allowed");
            throw new IllegalArgumentException("Cannot divide by zero");
        }
        logger.info("Dividing {} by {}", a, b);
        return a / b;
    }
}

Step 4: Main Application

This is the entry point for the project and demonstrates the simple instantiation and use of the CalculatorService.

Java
package com.gfg;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogAssertionExampleApplication {
    private static final Logger logger = LoggerFactory.getLogger(LogAssertionExampleApplication.class);

    public static void main(String[] args) {
        logger.info("Starting LogAssertionExampleApplication...");

        CalculatorService calculatorService = new CalculatorService();
        int sum = calculatorService.add(5, 3);
        logger.info("Result of addition: {}", sum);

        try {
            int division = calculatorService.divide(10, 2);
            logger.info("Result of division: {}", division);
        } catch (IllegalArgumentException e) {
            logger.error("Exception occurred during division: {}", e.getMessage());
        }

        try {
            calculatorService.divide(10, 0); // Causes an exception
        } catch (IllegalArgumentException e) {
            logger.error("Exception occurred during division by zero: {}", e.getMessage());
        }

        logger.info("LogAssertionExampleApplication finished.");
    }
}

Step 5: Logger Configuration in logback.xml

Add logback.xml to configure the logging output format:

XML
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

Step 6: Create CalculatorServiceTest.java

Create the CalculatorServiceTest class and this class contains the tests for the CalculatorService, including the assertions for log messages.

Java
package com.gfg;

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

public class CalculatorServiceTest {
    private final TestLogger testLogger = new TestLogger();

    @Test
    public void testAddLogs() {
        CalculatorService calculatorService = new CalculatorService();
        testLogger.startCapturing();

        calculatorService.add(5, 3);

        String logMessage = testLogger.getLog();
        assertTrue(logMessage.contains("Adding 5 and 3"), "Log message not found: " + logMessage);

        testLogger.stopCapturing();
    }

    @Test
    public void testDivideLogs() {
        CalculatorService calculatorService = new CalculatorService();
        testLogger.startCapturing();

        try {
            calculatorService.divide(10, 2);
        } catch (IllegalArgumentException e) {
            // Expected exception
        }

        String logMessage = testLogger.getLog();
        assertTrue(logMessage.contains("Dividing 10 by 2"), "Log message not found: " + logMessage);

        testLogger.stopCapturing();
    }
}

Step 7: Implement TestLogger.java

TestLogger is used to capture log messages during tests:

Java
package com.gfg;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.slf4j.LoggerFactory;

public class TestLogger {
    private final ListAppender<ILoggingEvent> listAppender = new ListAppender<>();

    public void startCapturing() {
        Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
        listAppender.start();
        rootLogger.addAppender(listAppender);
    }

    public void stopCapturing() {
        Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
        rootLogger.detachAppender(listAppender);
        listAppender.stop();
    }

    public String getLog() {
        StringBuilder logMessages = new StringBuilder();
        for (ILoggingEvent event : listAppender.list) {
            logMessages.append(event.getFormattedMessage()).append("\n");
        }
        return logMessages.toString();
    }
}

Step 8: Testing Configuration (logback-test.xml)

Logback configuration for testing:

XML
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

Step 9: Running the Tests

Run the test cases using the following maven command:

mvn test

Output:

The output of the successful test run should indicate that the logs were correctly captured and verified.

Test Output


Next Article
Article Tags :

Similar Reads