Search

Dark theme | Light theme
Showing posts with label AssertJ. Show all posts
Showing posts with label AssertJ. Show all posts

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.

October 2, 2023

Awesome AssertJ: Using A Custom Representation For Objects

The assertion error messages from AssertJ will use the toString() method of an object to give more insight about why the assertion could have failed. If an object doesn’t override the toString() method the default implementation is Object#toString(). The default implementation will print out the class name and an hexadecimal value of hashCode separated by a @. This doesn’t give much information about the actual object. For classes we have control over we can always implement a toString() method, but for third party classes we may not be able to do that. In order to customize how an object is represented in assertion error messages, AssertJ allows us to provide a custom representation class for an object. The custom representation class must implement the org.assertj.core.presentation.Representation interface. The interface has one method String toStringOf(Object) that should return a String representation of the object. If we want to keep the default behavior for other classes and only override it for our own class, we can extend the StandardRepresentation class and override the method String fallbackToStringOf(Object).

In the following example we provide a custom representation for the User class that will print the username property in the error messages instead of the default toString() implementation:

package mrhaki;

import org.assertj.core.api.Assertions;
import org.assertj.core.presentation.StandardRepresentation;
import org.junit.jupiter.api.Test;

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

class CustomRepresentation {

    // Helper class used in tests.
    private static class User {
        private final String username;

        private User(String username) {
            this.username = username;
        }
    }

    // Custom representation for the User class.
    // Here we can write how we want to represent the User class in
    // assertion error messages.
    private static class UserRepresentation extends StandardRepresentation {
        /**
         * The User class doesn't have a toString implementation, so we write
         * custom code so in assertion error message our object is nicely formatted.
         *
         * @param object the object to represent (never {@code null}
         * @return Custom string representation of User or standard representation for other objects.
         */
        @Override
        protected String fallbackToStringOf(Object object) {
            if (object instanceof User user) {
                return "User(username=%s)".formatted(user.username);
            }
            return super.fallbackToStringOf(object);
        }
    }

    @Test
    void assertUserObject() {
        final User mrhaki = new User("mrhaki");
        try {
            // assert will fail and the assertion error will contain
            // a default representation of the objects.
            assertThat(mrhaki).isEqualTo(new User("mrhaki42"));
        } catch (AssertionError e) {
            // expected: mrhaki.CustomRepresentation$User@126253fd
            // but was: mrhaki.CustomRepresentation$User@7eecb5b8
            assertThat(e).hasMessageContaining("expected: mrhaki.CustomRepresentation$User@")
                         .hasMessageContaining("but was: mrhaki.CustomRepresentation$User@");
        }
    }

    @Test
    void asserUserObjectWithCustomRepresentation() {
        final User mrhaki = new User("mrhaki");
        try {
            // assert will fail and the assertion error will contain
            // a custom representation of the objects only for this assert.
            assertThat(mrhaki).withRepresentation(new UserRepresentation()).isEqualTo(new User("mrhaki42"));
        } catch (AssertionError e) {
            // expected: User(username=mrhaki42)
            // but was: User(username=mrhaki)
            assertThat(e).hasMessageContaining("expected: User(username=mrhaki42)")
                         .hasMessageContaining("but was: User(username=mrhaki)");
        }
    }

    @Test
    void asserUserObjectWithCustomRepresentation2() {
        final User mrhaki = new User("mrhaki");
        try {
            // Set custom representation for User objects for all asserts.
            // This can also be defined at class level.
            Assertions.useRepresentation(new UserRepresentation());

            // assert will fail and the assertion error will contain
            // a custom representation of the objects.
            assertThat(mrhaki).isEqualTo(new User("mrhaki42"));
        } catch (AssertionError e) {
            // expected: User(username=mrhaki42)
            // but was: User(username=mrhaki)
            assertThat(e).hasMessageContaining("expected: User(username=mrhaki42)")
                         .hasMessageContaining("but was: User(username=mrhaki)");
        } finally {
            // Reset custom representation.
            Assertions.useDefaultRepresentation();
        }
    }

    @Test
    void asserUserObjectWithCustomRepresentationSAM() {
        final User mrhaki = new User("mrhaki");
        try {
            // assert will fail and the assertion error will contain
            // a custom representation of the objects for this assert only.
            assertThat(mrhaki).withRepresentation(new UserRepresentation()).isEqualTo(new User("mrhaki42"));
        } catch (AssertionError e) {
            // expected: User(username=mrhaki42)
            // but was: User(username=mrhaki)
            assertThat(e).hasMessageContaining("expected: User(username=mrhaki42)")
                         .hasMessageContaining("but was: User(username=mrhaki)");
        }
    }

    @Test
    void asserUserObjectWithCustomRepresentationSAM() {
        final User mrhaki = new User("mrhaki");
        try {
            // assert will fail and the assertion error will contain
            // a custom representation of the objects implemented
            // with the Representation interface as single abstract method (SAM).
            assertThat(mrhaki).withRepresentation(obj -> {
                if (obj instanceof User user) {
                    return "[User:username=%s]".formatted(user.username);
                } else {
                    return Objects.toIdentityString(obj);
                }
            }).isEqualTo(new User("mrhaki42"));
        } catch (AssertionError e) {
            // expected: [User:username=mrhaki42]
            // but was: [User:username=mrhaki]
            assertThat(e).hasMessageContaining("expected: [User:username=mrhaki42]")
                         .hasMessageContaining("but was: [User:username=mrhaki]");
        }
    }
}

Written with AssertJ 3.24.2

July 17, 2023

Awesome AssertJ: Writing Assertions For Optional

For a lot of types AssertJ has special assertion methods. Also for the type Optional. If we want to assert an Optional value we can use several methods that AssertJ provides. For example to check if an Optional is present we can use isPresent() or the alias isNotEmpty(). To check if the Optional is empty we can use isEmpty() or the alias isNotPresent(). Checking the value of an Optional (if it is indeed set) can be done with hasValue() or contains(). For more fine grained assertions on the value we can use hasValueSatisfying(Condition) or hasValueSatisfying(Consumer). With the map(Function) and flatMap(Function) methods we can map the Optional, if not empty, to another value and assert that value.

In the following example we see assertion methods for an Optional instance:

package mrhaki;

import org.assertj.core.api.Condition;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test;

import java.util.Optional;

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

class OptionalAssertions {
    
    @Test 
    void checkOptionalPresent(){
        assertThat(Optional.of("Awesome AssertJ"))
                // Check if Optional has a value.
                .isPresent()
                
                // Or we use the alias isNotEmpty().
                .isNotEmpty();
    }
    
    @Test 
    void checkOptionalNotPresent() {
        assertThat(Optional.empty())
                // Check if Optional is empty.
                .isEmpty()
                
                // Or we use the aliase isNotPresent().
                .isNotPresent();
    }
    
    @Test
    void checkValueOfOptional() {
        assertThat(Optional.of("Awesome AssertJ"))
                // We can check the value with hasValue(...).
                // We don't have to call get() ourselves.
                .hasValue("Awesome AssertJ")
                
                // Or we use the alias contains(...).
                .contains("Awesome AssertJ");
    }
    @Test
    void checkGetOfOptional() {
        // We can use get() to chain more assertions,
        // but we can only use generic Object assertions.
        assertThat(Optional.of("Awesome AssertJ"))
                .get()
                // We can only use Object assertions.
                .isEqualTo("Awesome AssertJ");

        // If we use get(InstanceOfAssertFactory) we get
        // back an object to use assertion methods for the 
        // given type.
        // The interface InstanceOfAssertFactories has a lot of 
        // useful constants to force the type returned by the Optional,
        // so we can use the assertions for that type.
        assertThat(Optional.of("Awesome AssertJ"))
                .get(InstanceOfAssertFactories.STRING)
                // Now we can use String assertions:
                .startsWith("Awesome")
                .endsWith("AssertJ");
    }

    @Test
    void checkValueSatisfyingOfConditional() {
        // We can use hasValueSatisfying(Consumer)
        // with the instance that is wrapped in the Optional
        // for more fine grained assertion.
        // Also, we can use assertions for the type wrapped
        // in the Optional.
        assertThat(Optional.of("Awesome AssertJ"))
                .hasValueSatisfying(s -> {
                    assertThat(s)
                            .startsWith("Awesome")
                            .endsWith("AssertJ");
                });
    }
    
    @Test
    void checkObjectValueOfOptionalWithCondition() {
        // Record to store properties of a framework.
        record Framework(String name, boolean awesome) {}

        // We can write a Condition that checks if the awesome
        // property is set to true.
        var isAwesomeFramework = new Condition<Framework>(Framework::awesome, "Framework is awesome!");

        // And with hasValueSatisfying we can use the Condition.
        assertThat(Optional.of(new Framework("AssertJ", true)))
                .hasValueSatisfying(isAwesomeFramework);
    }

    @Test
    void checkTransformedValueOfOptional() {
        // We can use map(...) to transform the value
        // of the optional (if present)
        // and write assertions for the transformed value.
        assertThat(Optional.of("Awesome AssertJ"))
                .map(String::toUpperCase)
                .hasValue("AWESOME ASSERTJ");

        // Record to store framework data with an optional type.
        record Framework(String name, Optional<Boolean> awesome) {}
        
        // With flatMap(...) we can transform the value
        // of the optional (if present) to another Optional
        // and write assertions for this transformed Optinal value.
        assertThat(Optional.of(new Framework("Awesome AssertJ", Optional.of(true))))
                .flatMap(Framework::awesome)
                .isPresent()
                .hasValue(true);
    }

    @Test
    void checkInstanceOfOptional() {
        // With containsInstanceOf we can write an assertion
        // to check the type that is contained in the Optional.
        assertThat(Optional.of("Awesome AssertJ"))
                .containsInstanceOf(String.class);
    }
}

Written with AssertJ 3.24.2.

July 12, 2023

Awesome AssertJ: Assertions For An URL Object

AssertJ has a lot of custom assertion methods for different types. For example to assert an URL object AssertJ gives us some specific methods. We can check for different components of the URL instance with different methods. For example we can check if the protocol is equal to the protocol we expect with hasProtocol(String). Similarly we can write assertions for the host, port, authority, path and anchor. To assert query parameters we can use hasQueryParameter(String) to check if query parameter is set and with hasQueryParameter(String, String) we can check if the query parameter has an expected value. To check the whole query string we can use hasQueryString(String).

Each of the assertion methods also has version to assert a component is not present. For example hasNoQuery() to assert a query is not defined for an URL.

In the following example we use the different assertion methods for an URL instance:

package mrhaki;

import org.junit.jupiter.api.Test;

import java.net.MalformedURLException;
import java.net.URL;

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

class UrlChecks {

    @Test
    void checkUrl() throws MalformedURLException {
        assertThat(new URL("https://www.mrhaki.com:80/blogs/index.html#groovy-goodness"))

                // We can check the protocol.
                .hasProtocol("https")

                // We can check the host name.
                .hasHost("www.mrhaki.com")

                // We can check the port if it is part of the URL.
                .hasPort(80)

                // We can check the authority (host + port).
                .hasAuthority("www.mrhaki.com:80")

                // We can check the path part of the URL.
                .hasPath("/blogs/index.html")

                // We can check the anchor part.
                .hasAnchor("groovy-goodness");
    }

    @Test
    void checkQueryParameters() throws MalformedURLException {
        assertThat(new URL("https://www.mrhaki.com/blogs/?tag=Groovy&text=Goodness"))

                // We can check the full query string.
                .hasQuery("tag=Groovy&text=Goodness")

                // We can check if a query parameter is defined.
                .hasParameter("tag")
                .hasParameter("text")

                // We can check if a query parameter with a given value is set.
                .hasParameter("tag", "Groovy")
                .hasParameter("text", "Goodness")

                // We can verify with another URL after the query parameters are sorted.
                .isEqualToWithSortedQueryParameters(new URL("https://www.mrhaki.com/blogs/?text=Goodness&tag=Groovy"));
    }

    @Test
    void checkUserInfo() throws MalformedURLException {
        assertThat(new URL("https://user:password@www.mrhaki.com/"))

                // We can check the user info part of the URL.
                .hasUserInfo("user:password");
    }

    @Test
    void checkUrlNegated() throws MalformedURLException {
        // We can also check for the non-presence of components of the URL.
        assertThat(new URL("https://www.mrhaki.com"))
                .hasNoPort()
                .hasNoPath()
                .hasNoQuery()
                .hasNoParameters()
                .hasNoParameter("tag")
                .hasNoParameter("tag", "Groovy")
                .hasNoAnchor()
                .hasNoUserInfo();

        assertThat(new URL("file:///Users/mrhaki"))
                .hasNoHost();
    }
}

Written with AssertJ 3.24.2.

July 5, 2023

Awesome AssertJ: Check Base64 Encoded Strings

AssertJ has some nice methods to verify string values. If we want to verify a string value is Base64 encoded we can use the isBase64String() method. We can leave out the padding of the value as it is optional. With the method asBase64Decoded() we can decode the value and write our assertions for the decoded value. The method asBase64Decoded() returns a byte[] object and we can use the asString() to convert it into a string value again.

In the following example we use the isBase64() and asBase64Decoded() methods:

package mrhaki;

import org.junit.jupiter.api.Test;

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

public class StringBase64 {

    @Test
    void checkValueIsBase64Encoded() {
        // We can simply check if the value is Base64 encoded
        // with the method isBase64().
        assertThat("QXNzZXJ0SiBiZSBBd2Vzb21lLCBtYXRleSE=").isBase64();

        // Padding (=) can be omitted from the Base64 encoded value.
        assertThat("QXNzZXJ0SiBiZSBBd2Vzb21lLCBtYXRleSE").isBase64();

        // If we want to check a value is NOT Base64 encoded we
        // need to check an AssertionError is thrown.
        assertThatExceptionOfType(AssertionError.class)
                .isThrownBy(() -> assertThat("AssertJ is Awesome").isBase64())
                .withMessageContaining("Expecting \"AssertJ is Awesome\" to be a valid Base64 encoded string");
    }

    @Test
    void checkBase64EncodedValue() {
        // We can use asBase64Decoded() to get a byte[] result
        // and apply our assertions.
        assertThat("QXNzZXJ0SiBiZSBBd2Vzb21lLCBtYXRleSE=")
                .asBase64Decoded().asString()
                .isEqualTo("AssertJ be Awesome, matey!");

        // Also here the padding (=) can be omitted.
        assertThat("QXNzZXJ0SiBiZSBBd2Vzb21lLCBtYXRleSE")
                .asBase64Decoded().asString()
                .isEqualTo("AssertJ be Awesome, matey!");

        assertThat("QXNzZXJ0SiBiZSBBd2Vzb21lLCBtYXRleSE=")
                .asBase64Decoded().asString()
                .isNotEqualTo("AssertJ is Awesome!");
    }
}

Written with AssertJ 3.24.2.

Awesome AssertJ: Use String Template To Verify String Value

To compare string values we can use the isEqualTo(String) method in AssertJ. But if we want to verify that a string contains a certain variable value we can use string templates. This makes the assertion more readable as we can see what value we expect in the string. To use string templates we must the method isEqualTo(String, Object…​). The first argument is the string template and the following arguments will be the actual values that should be used in the template. Actually the String.format(String, Object…​) method is used behind the scenes to format the string template, but we don’t have to clutter our assertions with that call.

In the following example we see how we can use string templates to verify string values:

package mrhaki;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

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

public class StringIsEqualToTemplate {

    @Test
    void verifyStringIsEqualTo() {
        // We can use a template to define the format of the String.
        // If we do actually String.format() is used to create
        // our expected value.
        assertThat("AssertJ is awesome!").isEqualTo("%s is %s!", "AssertJ", "awesome");
    }

    // Using a template can be very useful for example in parameterized tests.
    @ParameterizedTest
    @ValueSource(strings = {"AssertJ", "Spock"})
    void verifyStringFormat(String libraryName) {
        // given
        record Library(String name) {
            @Override
            public String toString() {
                return name;
            }
        }
        var library = new Library(libraryName);

        // expect
        assertThat(library.name() + " is awesome").isEqualTo("%s is awesome", library);
    }

    @Test
    void verifyStringIsNotEqualTo() {
        // There is no method implementation for isNotEqualTo where we can define
        // the format as first argument. But we can still use String.format
        // ourselves to achieve the same functionality.
        assertThat("AssertJ is awesome!").isNotEqualTo(String.format("%s is %s!", "Spock", "awesome"));
    }
}

Written with AssertJ 3.24.2.

July 3, 2023

Awesome AssertJ: Use returns To Verify An Object Using Functions

With the returns method in AssertJ we can verify an object using a function. This allows us to verify an object in a very flexible way. We can chain multiple returns method calls to verify multiple aspects of our object. The first argument of the returns method is the expected value of the function call. And the second argument is a function that calls a method on the object we want to verify. A simple function call would be a method reference using the class of the object. But we can also write our own function, where the argument of the function is actual object we are writing the assertion for. To verify the function doesn’t return an expected value we can use the method doesNotReturn.
We can also pass the function to the from method, available in the Assertions class. It can make the assertion more readeable as we can now read the code as: we expect the following value from calling this function.

In the following example we use the returns and doesNotReturn methods in several use cases:

package mrhaki;

import org.junit.jupiter.api.Test;

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

class Returns {

    // Pirate record we will use for tests.
    record Pirate(String name, String ship, int age) {}

    @Test
    void checkReturnValueMethodReference() {
        // With returns() we can specify the expected value
        // from a function call.
        // We can use a method references to easily
        // get a return value we want to verify.
        assertThat(new Pirate("Jack Sparrow", "Black Pearl", 40))
                .returns("Jack Sparrow", Pirate::name)
                .returns("Black Pearl", Pirate::ship)
                .returns(40, Pirate::age);
    }

    @Test
    void checkValueIsNotReturned() {
        // Instead of using returns() we can use doesNotReturn()
        // to verify the function doesn't return an expected value.
        assertThat(new Pirate("Will Turner", "Flying Dutchman", 35))
                .doesNotReturn("Jack Sparrow", Pirate::name)
                .doesNotReturn("Black Pearl", Pirate::ship)
                .doesNotReturn(40, Pirate::age);
    }

    @Test
    void checkReturnValueFrom() {
        // We can use the Assertions.from() method to add
        // some readability to our assertions.
        // We simply pass the function that should return our
        // expected value to the from() method.
        assertThat(new Pirate("Jack Sparrow", "Black Pearl", 40))
                .returns("Jack Sparrow", from(Pirate::name))
                .returns("Black Pearl", from(Pirate::ship))
                .returns(40, from(Pirate::age));
    }

    @Test
    void checkReturnValueFromFunction() {
        // Instead of using method references as functions
        // like in the first test,
        // we can use custom functions as well.
        // The type of the function argument is the object
        // we write the assertions for.
        assertThat(new Pirate("Jack Sparrow", "Black Pearl", 40))
                .returns(true, p -> p.name().startsWith("Jack"))
                .returns("black-pearl", p -> p.ship().toLowerCase().replace(" ", "-"))
                // We can also from() here.
                .returns(12, from(p -> p.name().length()))
                .returns(false, p -> p.age() > 50);
    }
}

Written with AssertJ 3.24.2.

June 30, 2023

Awesome AssertJ: Check String Starts Or Ends With A Given Value

Writing assertions using the nice fluent API of AssertJ is a joy. Besides some of the basic assertions like isEqualTo AssertJ also has specific assertions for specific types. For example if we want write an assertion to check if a String value starts or ends with an expected value we can use the startsWith(String) or endsWith(String) methods. If we don’t care that a character is upper or lower case we can also use startsWithIgnoringCase(String) or endsWithIgnoringCase(String). Each of the methods also has a counterpart method to check the String value doesn’t start or end with an expected value. For example we can use doesNotStartWith(String) to assert a value does not start with the expected value.

Instead of using the specific startsWith…​(String) or endsWith…​(String) methods we can also use the more generic matches method. Now we need to specify a Pattern or Predicate that matches our requirement. This give some more flexbility but is also more verbose. To test there is not a match we can use the doesNotMatch method.

In the following example we write assertions to check if a String value starts with an expected value:

package mrhaki;

import org.junit.jupiter.api.Test;
import java.util.regex.Pattern;

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

class StringStartsWith {

    @Test
    void testStringStartsWith() {
        assertThat("Awesome AssertJ").startsWith("Awesome");

        // We can also check ignoring the casing of the String value.
        assertThat("Awesome AssertJ").startsWithIgnoringCase("aweSome");
    }

    @Test
    void testStringDoesNotStartWith() {
        // Or we check a String value doesn't start with an expected value.
        assertThat("Amazing Assertj").doesNotStartWith("Awesome");

        // We can also check ignoring the casing of the String value.
        assertThat("Amazing Assertj").doesNotStartWithIgnoringCase("aweSome");
    }

    @Test
    void testStringStartsWithRegex() {
        // We can use regular expressions as well, but it is more verbose.
        assertThat("Awesome AssertJ").matches(Pattern.compile("^Awesome.*"));

        // Also here we can ignore casing.
        assertThat("Awesome AssertJ").matches(Pattern.compile("^awesome.*", Pattern.CASE_INSENSITIVE));

        // And check String value doesn't start with expected value.
        assertThat("Amazing AssertJ").doesNotMatch(Pattern.compile("^Awesome.*"));
    }

    @Test
    void testStringStartsWithPredicate() {
        // We can use a Predicate as well, but it is more verbose.
        assertThat("Awesome AssertJ").matches(s -> s.startsWith("Awesome"));

        // We are more flexible as we can use for example specify an offset.
        assertThat("Awesome AssertJ").matches(s -> s.startsWith("some", 3));

        // If we want to ignore casing we have to first convert our value.
        assertThat("Awesome AssertJ").matches(s -> s.toLowerCase().startsWith("awesome"));

        // And check String value doesn't start with expected value.
        assertThat("Amazing AssertJ").matches(s -> !s.startsWith("Awesome"));
    }
}

And in the next example we write assertions to check if a String value ends with an expected value:

package mrhaki;

import org.junit.jupiter.api.Test;
import java.util.regex.Pattern;

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

class StringEndsWith {

    @Test
    void testStringEndsWith() {
        assertThat("Awesome AssertJ").endsWith("AssertJ");

        // We can also check ignoring the casing of the String value.
        assertThat("Awesome AssertJ").endsWithIgnoringCase("assertJ");
    }

    @Test
    void testStringDoesNotEndWith() {
        // Or we we check our String values doesn't end with an expected value.
        assertThat("Awesome Asciidoc").doesNotEndWith("AssertJ");

        // We can also check ignoring the casing of the String value.
        assertThat("Awesome Asciidoc").doesNotEndWithIgnoringCase("assertJ");
    }

    @Test
    void testStringEndsWithRegex() {
        // We can use regular expressions as well, but it is more verbose.
        assertThat("Awesome AssertJ").matches(Pattern.compile(".*AssertJ$"));

        // Also here we can ignore casing.
        assertThat("Awesome AssertJ").matches(Pattern.compile(".*AssertJ$", Pattern.CASE_INSENSITIVE));

        // And check String value doesn't end with expected value.
        assertThat("Amazing Asciidoc").doesNotMatch(Pattern.compile(".*AssertJ$"));
    }

    @Test
    void testStringEndsWithPredicate() {
        // We can use a Predicate as well, but it is more verbose.
        assertThat("Awesome AssertJ").matches(s -> s.endsWith("AssertJ"));

        // If we want to ignore casing we have to first convert our value.
        assertThat("Awesome AssertJ").matches(s -> s.toLowerCase().endsWith("assertj"));

        // And check String value doesn't end with expected value.
        assertThat("Awesome Asciidoc").matches(s -> !s.endsWith("AssertJ"));
    }
}

Written with AssertJ 3.24.2.

June 29, 2023

Awesome AssertJ: Assert toString Method of Object With hasToString Method

AssertJ has many useful methods to write assertions using a fluid API. If we want to test the toString() method implementation of an object we can of course invoke the toString() method inside an assertThat expression and check with the assert method isEqualTo(String) the value. But AssertJ can make that easier so we don’t have to invoke toString() method ourselves. We can use the assert method hasToString(String) on the object we defined in the assertThat expression and specify our expected value as argument to the method. If we want to assert the toString() method doesn’t return an expected value we can use the assert method doesNotHaveToString(String).

In the following example test we use the methods hasToString and doesNotHaveToString for different

package mrhaki;

import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.Map;

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

class HasToString {

    // Helper class used in tests to check toString().
    private static class User {
        private final String username;

        public User(String username) {
            this.username = username;
        }

        public String toString() {
            return String.format("[User{username=%s}]", username);
        }
    }

    @Test
    void testHasToString() {
        // We can assert the toString() method of arbitrary objects
        // with hasToString(String).
        assertThat(new User("mrhaki")).hasToString("[User{username=mrhaki}]");
    }

    @Test
    void testDoesNotHaveString() {
        // If we want to assert the toString() method doesn't return a value
        // we can use doesNotHaveToString(String).
        assertThat(new User("mrhaki")).doesNotHaveToString("User[user=mrhaki]");
    }

    @Test
    void testIntegerToString() {
        // Simple types are automatically boxed to the object wrapper type
        // and then have a toString() method to assert the value for.
        assertThat(42).hasToString("42");  // int value is boxed to Integer.
        assertThat(Integer.valueOf(42)).hasToString("42");
    }

    @Test
    void testListToString() {
        // We can test toString() on List instances.
        var items = List.of("TDD", "JUnit", "AssertJ");
        assertThat(items).hasToString("[TDD, JUnit, AssertJ]");
        assertThat(items).doesNotHaveToString("[TDD,JUnit,AssertJ");
    }

    @Test
    void testMapToString() {
        // We can test toString() on Map instances.
        var map = Map.of("language", "Java");
        assertThat(map).hasToString("{language=Java}");
        assertThat(map).doesNotHaveToString("[language:Java]");
    }
}

Written with AssertJ 3.24.2.