<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Léa ANDRE</title>
    <description>The latest articles on Forem by Léa ANDRE (@la_andre_2fbbf84e88ae2f2).</description>
    <link>https://forem.com/la_andre_2fbbf84e88ae2f2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1765855%2Fc7e9bc2c-26f4-4642-9819-c71fc74ad7aa.jpg</url>
      <title>Forem: Léa ANDRE</title>
      <link>https://forem.com/la_andre_2fbbf84e88ae2f2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/la_andre_2fbbf84e88ae2f2"/>
    <language>en</language>
    <item>
      <title>Integration Testing with Failsafe and Merging Reports with JaCoCo in a Spring Boot Project</title>
      <dc:creator>Léa ANDRE</dc:creator>
      <pubDate>Thu, 05 Jun 2025 08:07:02 +0000</pubDate>
      <link>https://forem.com/la_andre_2fbbf84e88ae2f2/integration-testing-with-failsafe-and-merging-reports-with-jacoco-in-a-spring-boot-project-1958</link>
      <guid>https://forem.com/la_andre_2fbbf84e88ae2f2/integration-testing-with-failsafe-and-merging-reports-with-jacoco-in-a-spring-boot-project-1958</guid>
      <description>&lt;p&gt;In one of my recent backend projects built with Spring Boot, I needed to set up a robust testing strategy that included both unit and integration tests. I also wanted a clear view of my overall code coverage, regardless of whether the tests were unit or integration ones.&lt;/p&gt;

&lt;p&gt;This article explains how I configured Maven with the &lt;code&gt;failsafe&lt;/code&gt; plugin for integration testing and how I merged both unit and integration test coverage reports using &lt;code&gt;jacoco-maven-plugin&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Failsafe?
&lt;/h2&gt;

&lt;p&gt;By default, Maven’s &lt;code&gt;surefire&lt;/code&gt; plugin runs all test classes during the &lt;code&gt;test&lt;/code&gt; phase. However, this includes only unit tests. If you run integration tests here, they might fail due to the app not being fully initialized or due to side effects.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;failsafe&lt;/code&gt; plugin is designed specifically to run integration tests separately during the &lt;code&gt;integration-test&lt;/code&gt; and &lt;code&gt;verify&lt;/code&gt; phases, after the application has been properly built and started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Here’s a simplified overview of the relevant sections in my &lt;code&gt;pom.xml&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failsafe Plugin
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-failsafe-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.0.0-M5&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;integration-test&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;verify&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This plugin ensures that only test classes following the naming convention &lt;code&gt;*IT.java&lt;/code&gt; or &lt;code&gt;*ITCase.java&lt;/code&gt; are executed during the integration phase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2u7xqm5cuf7vh4x3sckc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2u7xqm5cuf7vh4x3sckc.png" alt="Integration tests files exemples" width="516" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  JaCoCo Plugin with Merged Reports
&lt;/h3&gt;

&lt;p&gt;I used JaCoCo to generate coverage reports for both unit and integration tests, and then merged them into a single report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jacoco&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jacoco-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.8.9&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- For unit tests --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;pre-unit-tests&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;prepare-agent&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;post-unit-test&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;report&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- For integration tests --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;pre-integration-tests&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;prepare-agent-integration&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;post-integration-tests&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;post-integration-test&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;report-integration&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Merging reports --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;merge&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;verify&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;merge&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;fileSets&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;fileSet&lt;/span&gt; &lt;span class="na"&gt;implementation=&lt;/span&gt;&lt;span class="s"&gt;"org.apache.maven.shared.model.fileset.FileSet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;directory&amp;gt;&lt;/span&gt;${project.basedir}&lt;span class="nt"&gt;&amp;lt;/directory&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;**/*.exec&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/fileSet&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/fileSets&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;destFile&amp;gt;&lt;/span&gt;target/jacoco/allTest.exec&lt;span class="nt"&gt;&amp;lt;/destFile&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Generate final report --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;post-merge-report&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;verify&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;report&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dataFile&amp;gt;&lt;/span&gt;target/jacoco/allTest.exec&lt;span class="nt"&gt;&amp;lt;/dataFile&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outputDirectory&amp;gt;&lt;/span&gt;target/jacoco/&lt;span class="nt"&gt;&amp;lt;/outputDirectory&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup generates separate reports for unit and integration tests, merges the &lt;code&gt;.exec&lt;/code&gt; data files, and creates a final report at the end of the build in &lt;code&gt;target/jacoco&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Run the Tests
&lt;/h2&gt;

&lt;p&gt;To execute both types of tests and generate the final coverage report, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn clean verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Unit tests are run during the &lt;code&gt;test&lt;/code&gt; phase.&lt;/li&gt;
&lt;li&gt;Integration tests are executed in &lt;code&gt;integration-test&lt;/code&gt; and verified in &lt;code&gt;verify&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The final coverage report is available in &lt;code&gt;target/jacoco/index.html&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using Failsafe for integration tests allows you to maintain clean separation between unit and integration tests. Combining it with JaCoCo gives you an accurate view of your test coverage across all types of tests.&lt;/p&gt;

&lt;p&gt;This setup has been essential in ensuring my backend remains well-tested and reliable. If you’re building Spring Boot applications with Maven, I highly recommend giving this configuration a try.&lt;/p&gt;

&lt;p&gt;Let me know in the comments how you manage your testing strategy or if you have any tips to improve this setup.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>testing</category>
      <category>java</category>
    </item>
    <item>
      <title>Capturing and Testing Logs in Java with SLF4J and Logback: A Simple Guide</title>
      <dc:creator>Léa ANDRE</dc:creator>
      <pubDate>Wed, 08 Jan 2025 11:19:26 +0000</pubDate>
      <link>https://forem.com/la_andre_2fbbf84e88ae2f2/capturing-and-testing-logs-in-java-with-slf4j-and-logback-a-simple-guide-4la5</link>
      <guid>https://forem.com/la_andre_2fbbf84e88ae2f2/capturing-and-testing-logs-in-java-with-slf4j-and-logback-a-simple-guide-4la5</guid>
      <description>&lt;p&gt;When working on Java projects, logging is a vital tool for debugging and understanding application behavior. In some cases, you might want to write tests that verify specific log messages are produced under certain conditions. Here’s a simple guide to achieving that using SLF4J with Logback and a custom TestLogAppender.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Example
&lt;/h2&gt;

&lt;p&gt;We’ll create a basic service that logs an error when an exception occurs, and a corresponding test to verify the log message.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Add Logback Test Dependency
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;...
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;ch.qos.logback&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;logback-core&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Implementing the Service
&lt;/h3&gt;

&lt;p&gt;Here’s a simple service that catches exceptions and logs an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.loggingdemo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.slf4j.Logger&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.slf4j.LoggerFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SimpleService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;performTask&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Simulate some task that fails&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Simulated exception"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IllegalArgumentException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An error occurred: {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Creating a Custom TestLogAppender
&lt;/h3&gt;

&lt;p&gt;This appender captures log messages during tests, enabling assertions on their content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.loggingdemo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ch.qos.logback.classic.spi.ILoggingEvent&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ch.qos.logback.core.AppenderBase&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestLogAppender&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AppenderBase&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ILoggingEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;StringBuilder&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StringBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ILoggingEvent&lt;/span&gt; &lt;span class="n"&gt;eventObject&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventObject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getFormattedMessage&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"
"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getLogs&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Writing the Test
&lt;/h3&gt;

&lt;p&gt;Now, let’s write a test to verify the log output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.loggingdemo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Assertions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ch.qos.logback.classic.Logger&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.junit.jupiter.api.BeforeEach&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.junit.jupiter.api.Test&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.slf4j.LoggerFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleServiceTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;TestLogAppender&lt;/span&gt; &lt;span class="n"&gt;logAppender&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;SimpleService&lt;/span&gt; &lt;span class="n"&gt;simpleService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@BeforeEach&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Attach TestLogAppender to the logger&lt;/span&gt;
        &lt;span class="n"&gt;logAppender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TestLogAppender&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;logAppender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SimpleService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAppender&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logAppender&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Initialize the service&lt;/span&gt;
        &lt;span class="n"&gt;simpleService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleService&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testPerformTaskLogsError&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Act&lt;/span&gt;
        &lt;span class="n"&gt;simpleService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;performTask&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Assert&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logAppender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogs&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An error occurred: Simulated exception"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;br&gt;
Don't forget to start your log appender !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 5: Running the Test
&lt;/h3&gt;

&lt;p&gt;When you run this test, it will confirm that the SimpleService logs the expected error message when an exception occurs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom Appender:&lt;/strong&gt; The &lt;code&gt;TestLogAppender&lt;/code&gt; captures log messages by overriding the &lt;code&gt;append&lt;/code&gt; method of Logback’s &lt;code&gt;AppenderBase&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logger Configuration:&lt;/strong&gt; During tests, we dynamically attach the custom appender to the target logger.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assertions on Logs:&lt;/strong&gt; Use the captured logs for assertions in your tests, verifying specific messages or patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With this approach, you can confidently test your logging behavior, ensuring your application logs important events and errors as expected. This setup is versatile and can be adapted to more complex scenarios, such as testing log levels or message formatting.&lt;/p&gt;

&lt;p&gt;Feel free to try this out and adapt it to your own projects!&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>java</category>
      <category>testing</category>
      <category>spring</category>
      <category>slf4j</category>
    </item>
  </channel>
</rss>
