Search

Dark theme | Light theme

October 12, 2024

Helidon SE Helpings: Starting Web Server On A Random Port

Helidon SE provides a web server using Java virtual threads. When we configure the web server we can specify a specific port number the server will listen on for incoming request. If we want to use a random port number we must specify the value 0. Helidon will then start the web server on a random port number that is available on our machine.

The following example shows how to start a web server on a random port number. We use Helidon SE to write our code:

package mrhaki.helidon;

import io.helidon.logging.common.LogConfig;
import io.helidon.webserver.WebServer;

public class Application {
    public static void main(String[] args) {
        // Load logging configuration.
        LogConfig.configureRuntime();

        // Configure web server on a random port number.
        WebServer server = WebServer.builder()
            .port(0)  // Use random port number
            .build()
            .start();

        // Print port number the server is listening on.
        System.out.println("WEB server is up at http://localhost:" + server.port());
    }
}

When we start our application we see the following output:

2024.10.11 17:27:36.005 Logging at runtime configured using classpath: /logging.properties
2024.10.11 17:27:36.606 Helidon SE 4.1.2 features: [Config, Encoding, Media, WebServer]
2024.10.11 17:27:36.621 [0x2326f965] http://0.0.0.0:61685 bound for socket '@default'
2024.10.11 17:27:36.639 Started all channels in 28 milliseconds. 863 milliseconds since JVM startup. Java 21.0.4+7-LTS
WEB server is up at http://localhost:61685

The next time we start our application we see a different port number:

2024.10.11 17:28:11.283 Logging at runtime configured using classpath: /logging.properties
2024.10.11 17:28:11.852 Helidon SE 4.1.2 features: [Config, Encoding, Media, WebServer]
2024.10.11 17:28:11.873 [0x28b386dd] http://0.0.0.0:61698 bound for socket '@default'
2024.10.11 17:28:11.892 Started all channels in 41 milliseconds. 835 milliseconds since JVM startup. Java 21.0.4+7-LTS
WEB server is up at http://localhost:61698

We can also use the Helidon Configuration API to configure the web server to use a random port number. We can for example set the port number to 0 in the application.yaml file. In the following example we initialize standard configuration and use it configure the webserver:

package mrhaki.helidon;

import io.helidon.config.Config;
import io.helidon.logging.common.LogConfig;
import io.helidon.webserver.WebServer;

public class Application {
    public static void main(String[] args) {
        // Load logging configuration.
        LogConfig.configureRuntime();

        // Initialize the configuration.
        Config config = Config.create();
        Config.global(config);

        // Configure web server on a random port number.
        WebServer server = WebServer.builder()
            .config(config.get("server"))  // Get port number from configuration.
            .build()
            .start();

        // Print port number the server is listening on.
        System.out.println("WEB server is up at http://localhost:" + server.port());
    }
}

With our new configuration we can use an environment variable SERVER_PORT to set the port number to 0 for our web server. The configuration could also be defined in an application.yaml file:

server:
  port: 0

Written with Helidon SE 4.2.1.

October 10, 2024

Spring Boot Sweets: Using Duration Type With Configuration Properties

With @ConfigurationProperties in Spring Boot we can bind configuration properties to Java classes. The class annotated with @ConfigurationProperties can be injected into other classes and used in our code. We can use the type Duration to configure properties that express a duration. When we set the value of the property we can use:

  • a long value with the unit to express milliseconds,
  • a value following the ISO-8601 duration format,
  • a special format supported by Spring Boot with the value and unit.

We can also use the @DurationUnit annotation to specify the unit for a long value. So instead of the default milliseconds we can specify the unit to be seconds or minutes for example. The unit is defined by the java.time.temporal.ChronoUnit enum and we pass it as an argument to the annotation.

The special format supported by Spring Boot supports the following units:

  • ns for nanoseconds,
  • us for microseconds,
  • ms for milliseconds,
  • s for seconds,
  • m for minutes,
  • h for hours,
  • d for days.

In the following example we define a record TimeoutProperties annotated with @ConfigurationProperties and four properties of type Duration. The property idleTimeout has the @DurationUnit annotation to specify the unit to be seconds.

package mrhaki;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;

import java.time.Duration;
import java.time.temporal.ChronoUnit;

@ConfigurationProperties(prefix = "timeout")
public record TimeoutProperties(
    Duration connectionTimeout,
    Duration readTimeout,
    Duration writeTimeout,
    @DurationUnit(ChronoUnit.SECONDS) Duration idleTimeout
) {}

In our application.properties file we can set the values of the properties:

# long value (in milliseconds)
timeout.connection-timeout=5000

# ISO-8601 format
timeout.read-timeout=PT30S

# Spring Boot's format
timeout.write-timeout=1m

# value in seconds (due to @DurationUnit annotation)
timeout.idle-timeout=300

In the following test we test the values of the properties in the class TimeoutProperties.

package mrhaki;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;

import java.time.Duration;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
@TestPropertySource(properties = {
    "timeout.connection-timeout=5000", // use long value in milliseconds
    "timeout.read-timeout=PT30S", // use ISO-8601 duration format
    "timeout.write-timeout=1m", // use special format supported by Spring Boot
    "timeout.idle-timeout=300" // use long value in seconds (set by @DurationUnit)
})
class TimeoutPropertiesTest {

    @Autowired
    private TimeoutProperties timeoutProperties;

    @Test
    void testConnectionTimeout() {
        assertThat(timeoutProperties.connectionTimeout())
            .isEqualTo(Duration.ofMillis(5000))
            .isEqualTo(Duration.ofSeconds(5));
    }

    @Test
    void testReadTimeout() {
        assertThat(timeoutProperties.readTimeout())
            .isEqualTo(Duration.ofSeconds(30));
    }

    @Test
    void testWriteTimeout() {
        assertThat(timeoutProperties.writeTimeout())
            .isEqualTo(Duration.ofMinutes(1))
            .isEqualTo(Duration.ofSeconds(60));
    }


    @Test
    void testIdleTimeout() {
        assertThat(timeoutProperties.idleTimeout())
            .isEqualTo(Duration.ofSeconds(300))
            .isEqualTo(Duration.ofMinutes(5));
    }

    @Test
    void testTimeoutToMillis() {
        assertThat(timeoutProperties.connectionTimeout().toMillis()).isEqualTo(5000);
        assertThat(timeoutProperties.readTimeout().toMillis()).isEqualTo(30000);
        assertThat(timeoutProperties.writeTimeout().toMillis()).isEqualTo(60000);
        assertThat(timeoutProperties.idleTimeout().toMillis()).isEqualTo(300000);
    }
}

Written with Spring Boot 3.4.4.

October 8, 2024

Java Joy: Using JShell With Standard Input

The Java Development Kit (JDK) includes a tool called jshell that can be used to interactively test Java code. Normally we run jshell and type Java code to be executed from an interactive shell. But we can also use jshell as a tool on the command line to accept standard input containing Java code to be executed. The output of the code can be used as input for another tool on the command line. We run jshell - to run jshell and accept standard input. The simplest way to pass Java code to jshell is to use echo to print the Java code to standard output and pipe it to jshell.

The following example shows how to use jshell and echo to get the default time zone of our system:

$ echo 'System.out.println(TimeZone.getDefault().getID());' | jshell -
Europe/Amsterdam

In the following example we fetch all Java system properties using jshell and then use grep to find the system properties with file in the name:

$ echo 'System.getProperties().list(System.out)' | jshell - | grep file
file.separator=/
file.encoding=UTF-8

The next example uses multi-line standard input:

$ jshell - << EOF
int square(int n) {
    return n * n;
}

IntStream.range(1, 10).map(n -> square(n)).forEach(System.out::println);
EOF

1
4
9
16
25
36
49
64
81

Written with Java 21.0.4.

October 6, 2024

Spring Boot Sweets: Using Configuration Properties With DataSize

With @ConfigurationProperties in Spring Boot we can bind configuration properties to Java classes. The class annotated with @ConfigurationProperties can be injected into other classes and used in our code. We can use the type DataSize to configure properties that express a size in bytes. When we set the value of the property we can use a long value. The size is then in bytes as that is the default unit. We can also add a unit to the value. Valid units are B for bytes, KB for kilobytes, MB for megabytes, GB for gigabytes and TB for terabytes.

We can also use the @DataSizeUnit annotation to specify the unit of the property in our class annotated with @ConfigurationProperties. In that case a the value without a unit assigned to the property is already in the specified unit.

In the following example we define a record StorageProperties annotated with @ConfigurationProperties and two properties maxFileSize and cacheSize. The property maxFileSize has no unit assigned to the value so it will be in the default unit of bytes. The property cacheSize has the unit MB assigned so the value will be in megabytes.

package mrhaki;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;

@ConfigurationProperties(prefix = "storage")
public record StorageProperties(
    DataSize maxFileSize /* Default unit is bytes */,
    @DataSizeUnit(DataUnit.MEGABYTES) DataSize cacheSize /* Explicit unit is megabytes */
) {}

In the following test we test the values of the properties in the class StorageProperties. We set the value of the maxFileSize property to 10MB with an explicit unit and the value of the cacheSize property without an explicit unit. We also test the values of the properties in bytes.

package mrhaki;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
@TestPropertySource(properties = {
    "storage.max-file-size=10MB", // value with explicit unit
    "storage.cache-size=500" // value without explicit unit
})
class StoragePropertiesTest {

    @Autowired
    private StorageProperties storageProperties;

    @Test
    void testMaxFileSize() {
        assertThat(storageProperties.maxFileSize())
            .isNotNull()
            // The DataSize class has useful static methods
            // to get values for a certain unit.
            .isEqualTo(DataSize.ofMegabytes(10));
    }

    @Test
    void testCacheSize() {
        assertThat(storageProperties.cacheSize())
            .isNotNull()
            .isEqualTo(DataSize.of(500, DataUnit.MEGABYTES));
    }

    @Test
    void testValuesInBytes() {
        // We can use the toBytes() method to get the actual bytes.
        assertThat(storageProperties.maxFileSize().toBytes())
            .isEqualTo(10 * 1024 * 1024);
        assertThat(storageProperties.cacheSize().toBytes())
            .isEqualTo(500 * 1024 * 1024);
    }
}

Written with Spring Boot 3.3.4.

October 1, 2024

Awesome AssertJ: Using Our Own Assertions Class

AssertJ already provides many useful assertions for all kind of types. But sometimes we want to define our own assertions for our own types. We can define new assertions by extending the AbstractAssert class In this class we add methods that will check the values of our type. The names of the methods can reflect the domain model of our type. This can make our tests more readable and understandable.

The abstract class AbstractAssert has a constructor that takes two arguments. We must pass the object we want to test and the class of the assertion class. The object that is passed is assigned to the property actual of the class. It is good practice to add a static method assertThat to the class that will return a new instance of the assertion class. Next we can add our own methods to the class. Within these methods we write the code to assert the values of our type. If the assertion would fail we can use the failWithMessage method to provide a message to the user.

AbstractAssert has also useful methods like isNotNull and isNotEqualTo that we can use in our assertions.

In the following example we write a custom assertion class for the type Pirate. The class Pirate has a nested object of type Ship. We define these with the following two records:

// File: mrhaki/Ship.java
package mrhaki;

public record Ship(String name, String type, int crewSize) {}
// File: mrhaki/Pirate.java
package mrhaki;

public record Pirate(String name, String rank, Ship ship) {}

Next we create our custom assertion class PirateAssert. We extend the class AbstractAssert, add a static method assertThat and the two assertion methods hasName and belongsOnShipWithName:

// File: mrhaki/PirateAssert.java
package mrhaki;

import org.assertj.core.api.AbstractAssert;

import java.util.Objects;

/**
 * Custom assertion methods for a Pirate instance.
 */
public class PirateAssert extends AbstractAssert<PirateAssert, Pirate> {
    protected PirateAssert(Pirate pirate) {
        super(pirate, PirateAssert.class);
    }

    /**
     * Method to create a new instance of PirateAssert, using the
     * same naming as standard AssertJ methods.
     * @param pirate Pirate instance to assert.
     * @return New PirateAssert instance.
     */
    public static PirateAssert assertThat(Pirate pirate) {
        return new PirateAssert(pirate);
    }

    /**
     * Check if the given name is equal to the actual
     * pirate's name.
     * @param name Expected name of the pirate.
     * @return Current instance for fluent API.
     */
    public PirateAssert hasName(String name) {
        // First check the actual Pirate instance is not null.
        isNotNull();

        // The Pirate instance passed in the assertThat method
        // is assigned to a variable with the name actual.
        // For comparison, we need to use that name.
        if (!Objects.equals(actual.name(), name)) {
            // Create an assertion failure with a message
            // that will be shown when the name is not equal to the actual name.
            failWithMessage("Expected pirate's name to be <%s> but was <%s>", name, actual.name());
        }

        // For fluent API usage we return this.
        return this;
    }

    /**
     * Check if the given name is equal to the name of the ship
     * the pirate belongs to.
     * @param name Expected name of the ship.
     * @return Current instance for fluent API.
     */
    public PirateAssert belongsOnShipWithName(String name) {
        isNotNull();

        if (Objects.isNull(actual.ship())) {
            failWithMessage("Expected pirate to belong on ship with name <%s> but was not found on any ship", name);
        }

        if (!Objects.equals(actual.ship().name(), name)) {
            failWithMessage("Expected pirate to belong on ship with name <%s> but was <%s>", name, actual.ship().name());
        }

        return this;
    }
}

In the following test we use the custom assertion class PirateAssert to assert the values of a Pirate instance:

// File: mrhaki/CustomPirateAssertion.java
package mrhaki;

import org.junit.jupiter.api.Test;

import static mrhaki.PirateAssert.assertThat;

public class CustomPirateAssertion {

    @Test
    void checkPirate() {
        // given
        Ship ship = new Ship("Black Pearl", "Galleon", 100);
        Pirate pirate = new Pirate("Jack Sparrow", "Captain", ship);


        // expect
        assertThat(pirate).hasName("Jack Sparrow")
                          .belongsOnShipWithName("Black Pearl");
    }
}

Written with AssertJ 3.26.3.

September 30, 2024

Awesome AssertJ: Comparing Objects Recursively

To compare nested objects we can use the usingRecursiveComparison() method in AssertJ. We can set up the nested objects with values we expect, invoke a method that would return the actual nested objects, and then use the usingRecursiveComparison() method to compare the actual nested objects with the expected nested objects. This is a very clean way to compare nested objects. Also when we would add a new property to the nested objects our test would fail as we didn’t use that new property yet for our expected nested objects.

In the following example test we use the usingRecursiveComparison() method to compare actual nested objects with the expected nested objects. Our nested objects are of type Pirate and Ship.

// File: mrhaki/Ship.java
package mrhaki;

public record Ship(String name, String type, int crewSize) {}
// File: mrhaki/Pirate.java
package mrhaki;

public record Pirate(String name, String rank, Ship ship) {}

This is our class we want to test. The class PirateShipCreator creates the nested objects we want to write assertions for.

// File: mrhaki/PirateShipCreator.java
package mrhaki;

public class PirateShipCreator {
    public static Pirate createJackSparrow() {
        return new Pirate("Jack Sparrow", "Captain", createBlackPearl());
    }

    public static Pirate createDavyJones() {
        return new Pirate("Davy Jones", "Captain", createFlyingDutchmain());
    }

    private static Ship createBlackPearl() {
        return new Ship("Black Pearl", "Galleon", 100);
    }

    private static Ship createFlyingDutchmain() {
        return new Ship("Flying Dutchman", "Ghost ship", 199);
    }
}

The test class PirateShipCreatorTest uses the usingRecursiveComparison() method to compare actual nested objects with the expected nested objects:

// File: mrhaki/PirateShipCreatorTest.java
package mrhaki;

import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

public class PirateShipCreatorTest {

    @Test
    public void testPirateEquality() {
        // given
        Ship expectedShip = new Ship("Black Pearl", "Galleon", 100);
        Pirate expectedPirate = new Pirate("Jack Sparrow", "Captain", expectedShip);

        // when
        Pirate actualPirate = PirateShipCreator.createJackSparrow();

        // then
        // assert equality using recursive comparison
        assertThat(actualPirate)
            .usingRecursiveComparison()
            .isEqualTo(expectedPirate);
    }
}

If we want to ignore a property we can use the ignoringFields(String…​) method. Or if we want to ignore properties of a certain type we can use the ignoringFieldsOfTypes(Class<?>…​) method. This can be very useful for properties that store dates we cannot setup properly in our tests.

Instead of ignoring fields we can also specify which fields we want to compare with the comparingOnlyFields(String…​) method. And there is a comparingOnlyFieldsOfTypes(Class<?>…​) method to specify which fields of a certain type we want to compare.

In the following tests we use all four methods to ignore or include fields in our comparison.

// File: mrhaki/PirateShipCreatorTest.java
package mrhaki;

import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

public class PirateShipCreatorTest {

    @Test
    public void testPirateEqualityIgnoringShipCrewSize() {
        // given
        Ship expectedShip = new Ship("Flying Dutchman", "Ghost Ship", 100);
        Pirate expectedPirate = new Pirate("Davy Jones", "Captain", expectedShip);

        // when
        Pirate actualPirate = PirateShipCreator.createDavyJones();

        // then
        // assert equality using recursive comparison, ignoring crew size
        assertThat(actualPirate)
            .usingRecursiveComparison()
            .ignoringFields("ship.crewSize")
            .isEqualTo(expectedPirate);
    }

    @Test
    public void testPirateEqualityIgnoringIntegerFields() {
        // given
        Ship expectedShip = new Ship("Flying Dutchman", "Ghost Ship", 100);
        Pirate expectedPirate = new Pirate("Davy Jones", "Captain", expectedShip);

        // when
        Pirate actualPirate = PirateShipCreator.createDavyJones();

        // then
        // assert equality using recursive comparison, ignoring integer fields
        assertThat(actualPirate)
            .usingRecursiveComparison()
            .ignoringFieldsOfType(Integer.class)
            .isEqualTo(expectedPirate);
    }

    @Test
    public void testPirateEqualityComparingSelectedFields() {
        // given
        Ship expectedShip = new Ship("Flying Dutchman", "Ghost Ship", 100);
        Pirate expectedPirate = new Pirate("Davy Jones", "Captain", expectedShip);

        // when
        Pirate actualPirate = PirateShipCreator.createDavyJones();

        // then
        // assert equality using recursive comparison, comparing only selected fields
        assertThat(actualPirate)
            .usingRecursiveComparison()
            .comparingOnlyFields("name", "rank", "ship.name", "ship.type")
            .isEqualTo(expectedPirate);
    }

    @Test
    public void testPirateEqualityComparingSelectedTypeOfFields() {
        // given
        Ship expectedShip = new Ship("Flying Dutchman", "Ghost Ship", 100);
        Pirate expectedPirate = new Pirate("Davy Jones", "Captain", expectedShip);

        // when
        Pirate actualPirate = PirateShipCreator.createDavyJones();

        // then
        // assert equality using recursive comparison,
        // comparing only fields of type String and Ship
        assertThat(actualPirate)
            .usingRecursiveComparison()
            .comparingOnlyFieldsOfTypes(String.class, Ship.class)
            .isEqualTo(expectedPirate);
    }
}

Written with AssertJ 3.26.3.

May 21, 2024

Clojure Goodness: Extending is Macro With Custom Assertions

The is macro in the clojure.test namespace can be used to write assertions about the code we want to test. Usually we provide a predicate function as argument to the is macro. The prediction function will call our code under test and return a boolean value. If the value is true the assertion passes, if it is false the assertion fails. But we can also provide a custom assertion function to the is macro. In the clojure.test package there are already some customer assertions like thrown? and instance?. The assertions are implemented by defining a method for the assert-expr multimethod that is used by the is macro. The assert-expr multimethod is defined in the clojure.test namespace. In our own code base we can define new methods for the assert-expr multimethod and provide our own custom assertions. This can be useful to make tests more readable and we can use a language in our tests that is close to the domain or naming we use in our code.

The implementation of the custom assertion should call the function do-report with a map containing the keys :type, :message, :expected and :actual. The :type key can have the values :fail or :pass. Based on the code we write in our assertion we can set the value correctly. Mostly the :message key will have the value of the message that is defined with the is macro in our tests. The keys :expected and :actual should contain reference to what the assertion expected and the actual result. This can be a technical reference, but we can also make it a human readable reference.

In the following example we implement a new customer assertion jedi? that checks if a given name is a Jedi name. The example is based on an example that can be found in the AssertJ documentation.

(ns mrhaki.test
  (:require [clojure.test :refer [deftest is are assert-expr]]))

(defmethod assert-expr 'jedi?
  "Assert that a given name is a Jedi."
  [msg form]
  `(let [;; We get the name that is the second element in the form.
         ;; The first element is the symbol `'jedi?`.
         name# ~(nth form 1)
         ;; We check if the name is part of a given set of Jedi names.
         result# (#{"Yoda" "Luke" "Obiwan"} name#)
         ;; We create an expected value that is used in the assertion message.
         expected# (str name# " to be a jedi.")]
     (if result#
       (do-report {:type     :pass
                   :message  ~msg,
                   :expected expected#
                   :actual   (str name# " is actually a jedi.")})
       (do-report {:type     :fail
                   :message  ~msg,
                   :expected expected#
                   :actual   (str name# " is NOT a jedi.")}))
     result#))

;; We can use our custom assertion in our tests.
(deftest jedi
  (is (jedi? "Yoda")))

;; The custom assertion can also be used with
;; the are macro as it will expand into multiple
;; is macro calls.
(deftest multiple-jedi
  (are [name] (jedi? name)
    "Yoda" "Luke" "Obiwan"))

;; The following test will fail, so we can
;; see failure message with the :expected and :actual values.
(deftest fail-jedi
  (is (jedi? "R2D2") "Is it?"))

If we run our failing test we see in the output that the assertion message is using our definition of the expected and actual values:

...
 expected: "R2D2 to be a jedi."
   actual: "R2D2 is NOT a jedi."
...

Written with Clojure 1.11.3.

May 19, 2024

Clojure Goodness: Combine Multiple Test Cases With are

The clojure.test namespace has the are macro that allows us to combine multiple test cases for the code we want to test, without having to write multiple assertions. We can provide multiple values for a function we want to test together with the expected values. Then then macro will expand this to multiple expressions with the is macro where the real assertion happens. Besides providing the data we must also provide the predicate where we assert our code under test. There is a downside of the are macro and that is that in case of assertion failures the line numbers in the error message could be off.

The first argument of the are macro is a vector with symbols that represent the names of the variables we want to use in the predicate. The second argument is the predicate where we write our assertion for the code under test. The remaining arguments are values for the symbols in the first argument. We can provide multiple sets of values for the symbols in the first argument, and the are macro will use this when the macro is expanded into multiple expressions.

In the next example we use the are macro to test the full-name function.

(ns mrhaki.test
  (:require [clojure.test :refer [deftest are]]
            [clojure.string :as str]))

;; Function we want to test.
(defn is-palidrome?
  [s]
  (= s (str/reverse s)))

(deftest palidrome
  (are
   ;; Vector with symbol used in test expression
   [s]
   ;; Test expression where we test the is-palidrome? function
   (true? (is-palidrome? s))
    ;; Data for the input symbol s
    "radar"
    "kayak"
    "racecar"
    "madam"
    "refer"
    "step on no pets"))

In the following example we use a test expression where we also use an expected value that is provided with the input data:

(ns mrhaki.test
  (:require [clojure.test :refer [deftest are]]))

;; Function we want to test.
(defn full-name
  "Returns a full name"
  [first-name last-name]
  (str first-name " " last-name))

(deftest sample
  (are
   [first-name last-name result]
   (= (full-name first-name last-name) result)
    "Fat" "Tony" "Fat Tony"
    "Krusty" "the Clown" "Krusty the Clown"))

Written with Clojure 1.11.3.

April 21, 2024

Clojure Goodness: Pretty Printing Collection Of Maps

The namespace clojure.pprint has some useful function to pretty print different data structures. The function print-table is particularly useful for printing a collection of maps, where each map represents a row in the table, and the keys of the maps represent the column headers. The print-table function accepts the collection as argument and prints the table to the console (or any writer that is bound to the *out* var). We can also pass a vector with the keys we want to include in the table. Only the keys we specify are in the output. The order of the keys in the vector we pass as argument is also preserved in the generated output.

In the following example code we use the print-table function to print some conference data that is stored in a vector of maps. We use the function with-out-str to capture the output of print-table so we can compare it with our expected result. In the example we first call print-table with the full conferences vector, and then we call it again with a vector of specific keys we want to include in the output.

(ns mrhaki.sample.print-table
  (:require [clojure.pprint :refer [print-table]]
            [clojure.test :refer [is]])
  (:import [java.io StringWriter]))

;; Vector with maps representing conference information.
(def conferences [{:name "Javaland" :location "Nürburgring" :country "Germany"}
                  {:name "JFall" :location "Ede" :country "The Netherlands"}
                  {:name "Full Stack Conference" :location "Nieuwegein" :country "The Netherlands"}])

;; Using print-table we get a nicely formatted table with all
;; rows from our conferences vector.
;; Each key name is a column header.
;; We use with-out-str function to capture the output
;; of the print-table function as String value.
(is (= "
|                 :name |   :location |        :country |
|-----------------------+-------------+-----------------|
|              Javaland | Nürburgring |         Germany |
|                 JFall |         Ede | The Netherlands |
| Full Stack Conference |  Nieuwegein | The Netherlands |
" (with-out-str (print-table conferences))))

;; Using print-table with a vector of keys we get a nicely formatted table
;; with all rows from our conferences vector.
;; But now the columns are only the keys we specified.
;; The order of the keys is also the order of the columns.
;; We use with-out-str function to capture the output
;; of the print-table function as String value.
(is (= "
|        :country |                 :name |
|-----------------+-----------------------|
|         Germany |              Javaland |
| The Netherlands |                 JFall |
| The Netherlands | Full Stack Conference |
" (with-out-str (print-table [:country :name] conferences))))

Written with Clojure 1.11.2.

March 25, 2024

Mastering Mockito: Returning Fresh Stream For Multiple Calls To Mocked Method

When we mock a method that returns a Stream we need to make sure we return a fresh Stream on each invocation to support multiple calls to the mocked method. If we don’t do that, the stream will be closed after the first call and subsequent calls will throw exceptions. We can chain multiple thenReturn calls to return a fresh Stream each time the mocked method is invoked. Or we can use multiple arguments with the thenReturn method, where each argument is returned based on the number of times the mocked method is invoked. So on the first invocation the first argument is returned, on second invocation the second argument and so on. This works when we know the exact number of invocations in advance. But if we want to be more flexible and want to support any number of invocations, then we can use thenAnswer method. This method needs an Answer implementation that returns a value on each invocation. The Answer interface is a functional interface with only one method that needs to be implemented. We can rely on a function call to implement the Answer interface where the function gets a InvocationOnMock object as parameter and returns a value. As the function is called each time the mocked method is invoked, we can return a Stream that will be new each time.

In the following example we use chained method calls using thenReturn and we use thenAnwer to support multiple calls to our mocked method temperature that returns a Stream of Double values:

package mrhaki;

import org.junit.jupiter.api.Test;

import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class MockReturnMultipleStreams {

    // Simple interface to return a stream of
    // temperature values for a given city.
    interface Weather {
        Stream<Double> temperature(String city);
    }

    // Simple class that uses Weather interface.
    static class SubjectUnderTest {

        private final Weather weather;

        SubjectUnderTest(Weather weather) {this.weather = weather;}

        public String weatherReport(String city) {
            // By invoking the methods celcius and fahrenheit we will
            // invoke the weather.temperature method twice.
            return String.format("The temperature in %s is %.1f degrees Celcius or %.1f degrees Fahrenheit.",
                                 city, celcius(city), fahrenheit(city));
        }

        private double celcius(String city) {
            return weather.temperature(city).findAny().get();
        }

        private double fahrenheit(String city) {
            return (celcius(city) * 9/5)  + 32;
        }
    }

    private final Weather weather = mock(Weather.class);
    private final SubjectUnderTest subjectUnderTest = new SubjectUnderTest(weather);

    @Test
    void shouldReturnCorrectWeatherReport() {
        // given

        // Return type of the mocked method temperature is a Stream.
        // On the first call in the subjectUnderTest instance the stream
        // is closed, so the second call will give an exception that
        // the stream is already been operated upon or closed.
        // To support the second call we need to return a new stream
        // with the same content.
        // If we need to support more calls than two we need
        // to add more thenReturn statements.
        // See the next test method for an example with thenAnswer
        // that supports multiple calls more easily.
        double temperature = 21.0;
        when(weather.temperature("Tilburg"))
                // First call
                .thenReturn(Stream.of(temperature))
                // Second call
                .thenReturn(Stream.of(temperature));

        // Alternative syntax:
        // when(weather.temperature("Tilburg"))
        //        .thenReturn(Stream.of(temperature), Stream.of(temperature));

        // when
        String result = subjectUnderTest.weatherReport("Tilburg");

        // then
        assertThat(result).isEqualTo("The temperature in Tilburg is 21,0 degrees Celcius or 69,8 degrees Fahrenheit.");
    }

    @Test
    void shouldReturnCorrectWeatherReport2() {
        // given

        // Return type of the mocked method temperature is a Stream.
        // On the first call in the subjectUnderTest instance the stream
        // is closed, so the second call will give an exception that
        // the stream is already been operated upon or closed.
        // To support the second call we can use thenAnswer method
        // which will return a fresh Stream on each call.
        // Now the number of calls is not limited, because on each
        // invocation a fresh Stream is created.
        when(weather.temperature("Tilburg"))
                .thenAnswer(invocationOnMock -> Stream.of(21.0));

        // when
        String result = subjectUnderTest.weatherReport("Tilburg");

        // then
        assertThat(result).isEqualTo("The temperature in Tilburg is 21,0 degrees Celcius or 69,8 degrees Fahrenheit.");
    }
}

Written with Mockito 3.12.4.