<?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>Spring Builders: Tobias Haindl</title>
    <description>The latest articles on Spring Builders by Tobias Haindl (@tobhai).</description>
    <link>https://springbuilders.dev/tobhai</link>
    <image>
      <url>https://springbuilders.dev/images/nd7IrfLbtsKizcaFlG-5-4IZAOn4gakSslX3AHH7sc0/rs:fill:90:90/g:sm/mb:500000/ar:1/aHR0cHM6Ly9zcHJp/bmdidWlsZGVycy5k/ZXYvdXBsb2Fkcy91/c2VyL3Byb2ZpbGVf/aW1hZ2UvMzg1Lzk1/ZmEwNzEwLTQ2NGQt/NDk2ZC1hMTBmLWI2/MjhjZjc2ODljMC5q/cGVn</url>
      <title>Spring Builders: Tobias Haindl</title>
      <link>https://springbuilders.dev/tobhai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://springbuilders.dev/feed/tobhai"/>
    <language>en</language>
    <item>
      <title>Spring IO 2024: Day 1</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Thu, 30 May 2024 19:56:28 +0000</pubDate>
      <link>https://springbuilders.dev/tobhai/spring-io-2024-day-1-3g92</link>
      <guid>https://springbuilders.dev/tobhai/spring-io-2024-day-1-3g92</guid>
      <description>&lt;p&gt;Spring IO opened with an awesome keynote featuring various members of the Spring team. &lt;br&gt;
They showcased new additions to the ecosystem like Spring AI and Spring Modulith and presented features that have been recently added to the Spring Framework.&lt;/p&gt;

&lt;p&gt;My favorite quote from the keynote was: &lt;br&gt;
&lt;em&gt;Make Monoliths Modern Again&lt;/em&gt; by Cora Iberkleid.&lt;br&gt;
We should definitely work hard to avoid building big balls of (distributed) mud 🙂&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;This Ain’t Your Parents’ Java&lt;/em&gt; Venkat Subramiam discussed how Java is evolving continuously and coming up with neat language features allowing us and future generations of developer to write better and more maintainable code.&lt;br&gt;
Favorite quotes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;They call this casting, I call it punishment&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Don’t work for the compiler, let the compiler work for you&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://springbuilders.dev/kehrlann"&gt;@kehrlann&lt;/a&gt; did a great job in explaining core concepts of Spring Security like filters, the &lt;code&gt;Authentication&lt;/code&gt; and &lt;code&gt;AuthenticationProvider&lt;/code&gt; interface.&lt;br&gt;
Key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can become a Spring Security ninja by enabling TRACE logging for &lt;a href="http://org.springframework.security"&gt;&lt;code&gt;org.springframework.security&lt;/code&gt;&lt;/a&gt; and digging through the logs&lt;/li&gt;
&lt;li&gt;Writing some extra code for proper emoji support is worth it 🙂&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the lunch break &lt;a class="mentioned-user" href="https://springbuilders.dev/rieckpil"&gt;@rieckpil&lt;/a&gt; kicked of this Spring Boot testing workshop. &lt;br&gt;
He gave an excellent overview about the out-of-the box features provided by the Spring Boot Starter Test and presented useful testing libraries like WireMock and Testcontainers.&lt;/p&gt;

&lt;p&gt;Key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pay attention to the number of Spring contexts created during the execution of your test suite. 
Keep the number low to ensure fast feedback cycles.&lt;/li&gt;
&lt;li&gt;With some creativity you can even use Java testing libraries for excelling at your favorite browser game 😉&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Аlina Yurenko talked about the past, the present and the future of GraalVM in her session and demonstrated the power of going native with the help of benchmarking demos.&lt;/p&gt;

&lt;p&gt;Key takeaway:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always use GraalVM ! 😉 (Since it provides a high-performance JIT compiler as well)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the final session of the day, &lt;a class="mentioned-user" href="https://springbuilders.dev/jkuipers"&gt;@jkuipers&lt;/a&gt; emphasized that production-grade applications require more than just architecture and business logic to succeed.&lt;br&gt;
He provided useful and applicable tips for logging, caching and error handling.&lt;/p&gt;

&lt;p&gt;Key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start building your own Autoconfigurations to share functionality between projects/modules&lt;/li&gt;
&lt;li&gt;Save time and money by using proper caching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I thoroughly enjoyed today’s sessions, and I'm already looking forward to day 2 of Spring IO 2024.&lt;/p&gt;

&lt;p&gt;A big shout-out to all the speakers and the exceptional organization team (&lt;a class="mentioned-user" href="https://springbuilders.dev/sergialmar"&gt;@sergialmar&lt;/a&gt; and crew) for making this conference both possible and fantastic.&lt;/p&gt;

</description>
      <category>java</category>
      <category>spring</category>
      <category>springboot</category>
      <category>springio</category>
    </item>
    <item>
      <title>Bending Time in Spring Applications</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Fri, 19 Apr 2024 12:33:04 +0000</pubDate>
      <link>https://springbuilders.dev/tobhai/bending-time-in-spring-applications-1mbg</link>
      <guid>https://springbuilders.dev/tobhai/bending-time-in-spring-applications-1mbg</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclaimer: this post was orginally published on &lt;a href="https://dev.to/tobhai/bending-time-in-spring-applications-33c8"&gt;dev.to&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Did you ever feel the need to unleash your inner Doctor Strange and manipulate time concisely when testing your Spring application?&lt;/p&gt;

&lt;p&gt;Let's check out the newest addition to the Spring ecosystem!&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring Modulith
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Spring Modulith supports developers implementing logical modules in Spring Boot applications. It allows them to apply structural validation, document the module arrangement, run integration tests for individual modules, observe the modules' interaction at runtime and generally implement module interaction in a loosely-coupled way.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://docs.spring.io/spring-modulith/docs/0.6.0/reference/html/#fundamentals"&gt;Documentation&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this article we will focus on one specific area of Modulith, the &lt;strong&gt;Moments&lt;/strong&gt; API.&lt;/p&gt;
&lt;h2&gt;
  
  
  Moments
&lt;/h2&gt;

&lt;p&gt;The Moments API enables developers to easily react to the passage of time-based events in your application. &lt;/p&gt;

&lt;p&gt;This allows you to easily write code which should be executed once a day, once a week etc.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example application
&lt;/h2&gt;

&lt;p&gt;I created a small sample application demonstrating features of the Moments API.&lt;br&gt;&lt;br&gt;
The code can be found on &lt;a href="https://github.com/tobHai/spring-modulith-samples"&gt;Github&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Let's walk through the application code together.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;First we need to add the Modulith dependencies to our Spring application. &lt;br&gt;
I'm using Maven to manage the dependencies in the sample application:&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;dependencyManagement&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&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;org.springframework.experimental&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;spring-modulith-bom&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.6.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&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;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the actual dependency:&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;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.experimental&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;spring-modulith-moments&lt;span class="nt"&gt;&amp;lt;/artifactId&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;p&gt;Of course, we also want to test our application code, so we will add the test dependency as well:&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;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.experimental&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;spring-modulith-starter-test&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;
  
  
  Daily notifications
&lt;/h3&gt;

&lt;p&gt;In the example application we want to send out notifications daily to our customers. &lt;br&gt;
At the end of each day, we want to fetch all our customers and check if they opted in for receiving a notification on this day.&lt;/p&gt;

&lt;p&gt;Let's create a simple Spring service called &lt;code&gt;CustomerNotificationService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To listen to events emitted by Moments, we annotate a public method with &lt;code&gt;@EventListener&lt;/code&gt; and add the event we want to listen to as a method parameter:&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="nd"&gt;@EventListener&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;on&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;DayHasPassed&lt;/span&gt; &lt;span class="n"&gt;dayHasPassed&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;p&gt;Now we can easily implement our business logic and let Moments take care of the rest:&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="nd"&gt;@EventListener&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;on&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;DayHasPassed&lt;/span&gt; &lt;span class="n"&gt;dayHasPassed&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;passedDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dayHasPassed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{} has passed. Checking notifications for customers."&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passedDate&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;customerService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCustomers&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allowedNotificationDays&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="n"&gt;passedDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDayOfWeek&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;eventPublisher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;publishEvent&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;CustomerNotificationEvent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;passedDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDayOfWeek&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;p&gt;For simplicity and brevity reasons the &lt;code&gt;CustomerService&lt;/code&gt; returns two hard-coded customers:&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="nd"&gt;@Service&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;CustomerService&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;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getCustomers&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="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&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;Customer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DayOfWeek&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MONDAY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DayOfWeek&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FRIDAY&lt;/span&gt;&lt;span class="o"&gt;)),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DayOfWeek&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SATURDAY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DayOfWeek&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SUNDAY&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;p&gt;If the customer opted in for receiving notifications on this day, we simply emit a new &lt;code&gt;CustomerNotificationEvent&lt;/code&gt; which could then be picked up by some other service and actual send out the notification. &lt;/p&gt;

&lt;p&gt;Since we care about our code, we want to ensure that it is working properly. Thankfully Modulith comes with great test support. &lt;br&gt;
Time for some time bending :)&lt;/p&gt;
&lt;h3&gt;
  
  
  Testing time
&lt;/h3&gt;

&lt;p&gt;The Moments API exposes a class called &lt;code&gt;TimeMachine&lt;/code&gt;. &lt;br&gt;
With it, we can easily manipulate time in our tests. &lt;br&gt;
In the following test we will enable the &lt;code&gt;TimeMachine&lt;/code&gt; by setting &lt;code&gt;spring.modulith.moments.enable-time-machine=true&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Additionally, we will annotate the test class with &lt;code&gt;@ApplicationModuleTest&lt;/code&gt;. This is another cool feature provided by Modulith. &lt;br&gt;
With it only beans defined in the given module (in our case &lt;code&gt;customer&lt;/code&gt;) will be created upon test execution.&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="nd"&gt;@ApplicationModuleTest&lt;/span&gt;
&lt;span class="nd"&gt;@Import&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CustomerNotificationServiceTestConfig&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="nd"&gt;@TestPropertySource&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"spring.modulith.moments.enable-time-machine=true"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomerNotificationServiceTest&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;TimeMachine&lt;/span&gt; &lt;span class="n"&gt;timeMachine&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;CustomerNotificationServiceTest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;TimeMachine&lt;/span&gt; &lt;span class="n"&gt;timeMachine&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timeMachine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timeMachine&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sendNotificationToCustomer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;PublishedEvents&lt;/span&gt; &lt;span class="n"&gt;publishedEvents&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;timeMachine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shiftBy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofDays&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&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;publishedEvents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CustomerNotificationEvent&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="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matching&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;-&amp;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;getCustomerId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;extracting&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;CustomerNotificationEvent:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getDayOfWeek&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;containsOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DayOfWeek&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MONDAY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DayOfWeek&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FRIDAY&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;publishedEvents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CustomerNotificationEvent&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="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matching&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;-&amp;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;getCustomerId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;extracting&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;CustomerNotificationEvent:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getDayOfWeek&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;containsOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DayOfWeek&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SATURDAY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DayOfWeek&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SUNDAY&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;h4&gt;
  
  
  Bootstrapping with ApplicationModuleTest
&lt;/h4&gt;

&lt;p&gt;If we run the test and check the logs we can see what &lt;code&gt;@ApplicationModuleTest&lt;/code&gt; does under the hood:&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="nc"&gt;Bootstrapping&lt;/span&gt; &lt;span class="nd"&gt;@org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;springframework&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modulith&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ApplicationModuleTest&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="nf"&gt;STANDALONE&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tobhai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modulithevents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ModulithEventsApplication&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Logical&lt;/span&gt; &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;dev.tobhai.modulithevents.customer&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Direct&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="nl"&gt;dependencies:&lt;/span&gt; &lt;span class="n"&gt;none&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Spring&lt;/span&gt; &lt;span class="nl"&gt;beans:&lt;/span&gt;
  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CustomerNotificationService&lt;/span&gt;
  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CustomerService&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the help of &lt;code&gt;@ApplicationModuleTest&lt;/code&gt; we can easily test the interaction between multiple beans defined in a module without the need for a full-blown Spring Context. &lt;/p&gt;

&lt;h4&gt;
  
  
  Shifting time in the test
&lt;/h4&gt;

&lt;p&gt;Now to the time-bending part of the test:&lt;br&gt;
First we define a fixed &lt;code&gt;Clock&lt;/code&gt; instance in the &lt;code&gt;CustomerNotificationServiceTestConfig&lt;/code&gt;.&lt;br&gt;
Then we enable the &lt;code&gt;TimeMachine&lt;/code&gt; by setting: &lt;code&gt;spring.modulith.moments.enable-time-machine&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we can simply inject the &lt;code&gt;TimeMachine&lt;/code&gt; instance into our test class and use it to shift time around in a very readable and concise way:&lt;br&gt;
&lt;code&gt;timeMachine.shiftBy(Duration.ofDays(1));&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Assertions on emitted events
&lt;/h4&gt;

&lt;p&gt;How can we actually verify that the &lt;code&gt;CustomerNotificationEvent&lt;/code&gt; was emitted properly?&lt;/p&gt;

&lt;p&gt;Modulith helps us out in this scenario as well:&lt;br&gt;
By adding &lt;code&gt;PublishedEvents publishedEvents&lt;/code&gt; to the signature of our test method, we can access all emitted events and perform assertions on them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;publishedEvents.ofType(CustomerNotificationEvent.class)&lt;br&gt;
                .matching(e -&amp;gt; e.getCustomerId() == 1)&lt;/code&gt; helps us to access all &lt;code&gt;CustomerNotificationEvent&lt;/code&gt;s for customer with ID. &lt;br&gt;
The time in our test starts on a Monday, and we shift "time" by seven days. &lt;br&gt;
Therefore, we expect that two events (for Monday and Friday) are emitted for customer 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;In this article we explored the Moments API of the Spring Modulith projects. &lt;br&gt;
Additionally, we had a look at testing support provided Modulith.&lt;/p&gt;

&lt;p&gt;Did you play around with Modulith yet? &lt;br&gt;
If so, what is your favorite feature?&lt;/p&gt;

&lt;p&gt;Cover photo by Aron Visuals on &lt;a href="https://unsplash.com/photos/BXOXnQ26B7o"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>modulith</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
