Search

Dark theme | Light theme

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.

March 8, 2024

Gradle Goodness: Organizing Tasks Using The Task Container

A Gradle build file describes what is needed to build our Java project. We apply one or more plugins, configure the plugins, declare dependencies and create and configure tasks. We have a lot of freedom to organize the build file as Gradle doesn’t really care. So to create maintainable Gradle build files we need to organize our build files and follow some conventions. In this post we focus on organizing the tasks and see if we can find a good way to do this.

It is good to have a single place where all the tasks are created and configured, instead of having all the logic scattered all over the build file. The TaskContainer is a good place to put all the tasks. To access the TaskContainer we can use the tasks property on the Project object. Within the scope of the tasks block we can create and configure tasks. Now we have a single place where all the tasks are created and configured. This makes it easier to find the tasks in our project as we have a single place to look for the tasks.

Within the scope of the TaskContainer we can use a convention to put the task creation methods at the top of the TaskContainer block. And the task configuration methods are after the task creation in the TaskContainer block. The tasks that are created at the top of the TaskContainer scope can be referenced by configuration code for tasks later in the TaskContainer scope.

The following diagram shows the build file structure and an example of the implementation:

In the example Gradle build file for a Java project we organize the tasks in the TaskContainer using this convention:

plugins {
    java
}
...
tasks {
    // ----------------------------------------------
    // Task creation at the top of the container.
    // ----------------------------------------------

    // Register new task "uberJar".
    val uberJar by registering(Jar::class) {
        archiveClassifier = "uber"

        from(sourceSets.main.get().output)

        dependsOn(configurations.runtimeClasspath)
        from({
            configurations.runtimeClasspath.get()
                .filter { it.name.endsWith("jar") }
                .map { zipTree(it) }
        })
    }

    // ----------------------------------------------
    // Task configuration after task creation.
    // ----------------------------------------------

    // The output of the "uberJar" tasks is part of
    // the output of the "assemble" task.
    // We can refer to the "assemble" task directly
    // as it is added by the Java plugin.
    assemble {
        // We can refer to the task name that
        // we just created in our
        // tasks configuration block.
        dependsOn(uberJar)
    }

    // Configure tasks with type JavaCompile.
    withType<JavaCompile>().configureEach {
        options.compilerArgs.add("--enable-preview")
    }
}
...

Although Gradle doesn’t enforce us to use this convention it can be very helpful as build file authors to use it as it makes it easier to find the tasks in the project.

Written with Gradle 8.6.

March 6, 2024

IntelliJ HTTP Client: Parsing JSON Web Tokens

The IntelliJ HTTP Client is very useful for testing APIs. We can use Javascript to look at the response and write tests with assertions about the response. If an API returns a JSON Web Token (JWT), we can use a Javascript function to decode the token and extract information from it. For example we can then assert that fields of the token have the correct value. There is no built-in support in IntelliJ HTTP Client to decode a JWT, but we can write our own Javascript function to do it. We then use the function in our Javascript response handler to decode the token.

In the following HTTP request file we simulate a call to get an response with a field containing an JWT. We use the function decodeJwt from the file jwt-utils.js to decode the token and extract information from it.

### Simulate a call to get an response with a field containing an JWT.
POST https://examples.http-client.intellij.net/anything
Content-Type: application/json

// Original token before it is base64 encoded:
// {
//     "sub": "1234567890",
//     "upn": "hubert@mrhaki.com",
//     "name": "mrhaki",
//     "groups": ["Blogger"],
//     "iat": 1516239022
// }
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidXBuIjoiaHViZXJ0QG1yaGFraS5jb20iLCJuYW1lIjoibXJoYWtpIiwiZ3JvdXBzIjpbIkJsb2dnZXIiXSwiaWF0IjoxNTE2MjM5MDIyfQ.9E2gYNFogs3K8pJH9JiJYISv403EtCm4tRzQWZi1CXM"
}

> {%
    import {decodeJwt} from './scripts/jwt-utils';

    // The token is in the response body and we get it
    // using the path `json.token`.
    // We store it as variable `token` so we can use in the next step.
    const token = decodeJwt(response.body.json.token);

    // We can write assertions on the token contents.
    client.test("Check fields in token", function () {
        client.assert(token.upn === "hubert@mrhaki.com");
        client.assert(token.name === "mrhaki");
        client.assert(token.groups.includes("Blogger"));
        client.assert(token.sub === "1234567890");
        client.assert(token.iat === 1516239022);
    });
%}

The function decodeJwt is defined in the file jwt-utils.js:

// File: ./scripts/jwt-utils.js
export function decodeJwt(token) {
    var base64EncodedPayload = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
    var jsonPayload = decodeURIComponent(decodeBase64(base64EncodedPayload)
        .split('')
        .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
        .join(''))
        // Remove any NUL characters at the end of the string.
        .replace(/\0+$/g, '');
    return JSON.parse(jsonPayload);
}

function decodeBase64(input) {
    const _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    let output = "";
    let chr1, chr2, chr3;
    let enc1, enc2, enc3, enc4;
    let i = 0;
    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
    while (i < input.length) {
        enc1 = _keyStr.indexOf(input.charAt(i++));
        enc2 = _keyStr.indexOf(input.charAt(i++));
        enc3 = _keyStr.indexOf(input.charAt(i++));
        enc4 = _keyStr.indexOf(input.charAt(i++));
        chr1 = (enc1 << 2) | (enc2 >> 4);
        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
        chr3 = ((enc3 & 3) << 6) | enc4;
        output = output + String.fromCharCode(chr1);
        if (enc3 !== 64) {
            output = output + String.fromCharCode(chr2);
        }
        if (enc4 !== 64) {
            output = output + String.fromCharCode(chr3);
        }
    }
    return decodeURI(output);
}

Written with IntelliJ IDEA 2023.3.4.

February 25, 2024

Gradle Goodness: Using System Properties Lazily

It is good practice in Gradle to use lazy configuration. This makes builds faster as only configuration values are evaluated when needed. We should try to not let Gradle spend time on evaluating configuration values that will not be used. For example tasks that are not executed could still be configured by Gradle. If we make sure the configuration of these tasks is lazy we can save time.

Gradle gives us a lazy way to get the value of a Java system property. In our build script we can use the providers property of type ProviderFactory and the method systemProperty(String). This method returns a Provider<String> instance that can be used to get the value of a system property in a lazy way. The method systemProperty can also be used with a Provider<String> argument.

In the following example we register a task that prints the value of the Java system property user.name to the console. We use lazy configuration to make sure the value of the system property is only fetched when the task is executed.

tasks {
    register<PrintSystemProperty>("printSystemProperty") {
        // We can use providers.systemProperty(String)
        // to get the value of an Java system property
        // in a lazy way.
        // The argument can also be a Provider<String> type.
        // So at this point the value is not fetched yet,
        // only when the task is executed the actual value
        // of the system property "user.name" is fetched.
        systemProperty = providers.systemProperty("user.name")
    }
}

// Simple task to print the value of a Java system property.
abstract class PrintSystemProperty : DefaultTask() {
    @get:Input
    abstract val systemProperty: Property<String> // Use lazy property.

    @TaskAction
    fun printSystemPropertyValue() {
        // Only here we actually will get the value
        // for the system property.
        logger.quiet(systemProperty.get())
    }
}
$ ./gradlew printSystemProperty

> Task :printSystemProperty
mrhaki

BUILD SUCCESSFUL in 685ms
2 actionable tasks: 2 executed

Written with Gradle 8.6.

Gradle Goodness: Using Environment Variables Lazily

It is good practice in Gradle to use lazy configuration. This makes builds faster as only configuration values are evaluated when needed. We should try to not let Gradle spend time on evaluating configuration values that will not be used. For example tasks that are not executed could still be configured by Gradle. If we make sure the configuration of these tasks is lazy we can save time.

Gradle gives us a lazy way to get the value of an environment variable. In our build script we can use the providers property of type ProviderFactory and the method environmentVariable(String). This method returns a Provider<String> instance that can be used to get the value of an environment variable in a lazy way.

In the following example we register a task that prints the value of the environment variable USER. We use lazy configuration to make sure the value of the environment variable is only fetched when the task is executed.

tasks {
    register<PrintEnvironmentVariable>("printEnvironmentVariable") {
        // We can use providers.environmentVariable(String)
        // to get the value of an environment variable
        // in a lazy way.
        // The argument can also be a Provider<String> type.
        // So at this point the value is not fetched yet,
        // only when the task is executed the actual value
        // of the environment variable "USER" is fetched.
        environmentVariable = providers.environmentVariable("USER")
    }
}

// Simple task to print the value of an environment variable.
abstract class PrintEnvironmentVariable : DefaultTask() {
    @get:Input
    abstract val environmentVariable: Property<String> // Use lazy property.

    @TaskAction
    fun printEnvironmentVariable() {
        // Only here we actually will get the value
        // for the environment variable.
        logger.quiet(environmentVariable.get())
    }
}

When we execute the tasks we see the value of the environment variable USER:

$ ./gradlew printEnvironmentVariable

> Task :printEnvironmentVariable
mrhaki

BUILD SUCCESSFUL in 599ms
2 actionable tasks: 2 executed

Written with Gradle 8.6.

February 14, 2024

IntelliJ HTTP Client: Accessing Environment Variables In JavaScript

When we use the IntelliJ HTTP Client we can write JavaScript for the pre-request and response handlers. If we want to access an environment variable in JavaScript we can use request.environment.get(string). The argument for the get function is the name of the environment variable we want to get the value for. Environment variables can be defined in the file http-client.env.json or in http-client.private.env.json.

In the following example we have environment file http-client.env.json with two environments development and production. Each environment has a variable named project:

{
    "development": {
        "project": "DEV-1270"
    },
    "production": {
        "project": "PROJ-1234"
    }
}

In our .http request file we are posting to endpoint https://ijhttp-examples.jetbrains.com/anything and we use JavaScript to read the environment variable project:

### POST to example URL
< {%
    // Get value for environment variable 'project'.
    const project = request.environment.get("project");

    // Use split to get the value before and after the -.
    const kindAndNumber = project.split("-");

    // As an example we use the value of the environment variable
    // to assign values to new request variables.
    request.variables.set("kind", kindAndNumber[0]);
    request.variables.set("number", kindAndNumber[1]);
  %}
POST https://ijhttp-examples.jetbrains.com/anything

{
    "project": "{{project}}",
    "kind": "{{kind}}",
    "number": "{{number}}"
}

In the response we see the variables do have values:

...
"json": {
    "kind": "PROJ",
    "number": "1234",
    "project": "PROJ-1234"
  }
...

Written with IntelliJ IDEA 2023.3.3.

February 11, 2024

IntelliJ HTTP Client: Using In-Place Variables

The built-in IntelliJ HTTP Client is very useful for testing HTTP requests and responses. If we want to define a variable in our .http file that is only used in this file and will not change per environment we can define it using the following syntax: @<variable name> = variable value. The variable is an in-place variable and the scope of the variable in the current .http file. The variable is immutable and can only be defined with a value once and cannot be overwritten. To refer to the variable we use the syntax {{<variable name>}}.

In the following example we define a variable with the name base-url and the value https://ijhttp-example.jetbrains.com:

# We define an in-place variable "base-url"
# to be used in this file.
@base-url = https://ijhttp-examples.jetbrains.com

### GET HTML page
GET {{base-url}}/html

### GET XML page
GET {{base-url}}/xml

### GET JSON page
GET {{base-url}}/json

### GET UUID4 response
GET {{base-url}}/uuid

Written with IntelliJ 2023.3.3.