package seedu.address.testutil;

/**
 * A set of assertion methods useful for writing tests.
 */
public class Assert {

    /**
     * Asserts that the {@code callable} throws the {@code expected} Exception.
     */
    public static void assertThrows(Class<? extends Throwable> expected, VoidCallable callable) {
        assertThrows(expected, null, callable);
    }

    /**
     * Asserts that the {@code callable} throws the {@code expectedException} and the {@code expectedMessage}.
     * If there's no need for the verification of the exception's error message, call
     * {@code assertThrows(Class<? extends Throwable>, VoidCallable)} instead.
     * {@see assertThrows(Class<? extends Throwable>, VoidCallable}
     */
    public static void assertThrows(Class<? extends Throwable> expectedException, String expectedMessage,
                                    VoidCallable callable) {
        try {
            callable.call();
        } catch (Throwable actualException) {
            String errorMessage;

            if (!actualException.getClass().isAssignableFrom(expectedException)) {
                errorMessage = String.format("Expected exception thrown: %s, actual: %s",
                        expectedException.getName(), actualException.getClass().getName());
            } else if (expectedMessage != null && !expectedMessage.equals(actualException.getMessage())) {
                errorMessage = String.format(
                        "Expected message thrown: %s, actual: %s", expectedMessage, actualException.getMessage());
            } else {
                return;
            }

            throw new AssertionError(errorMessage, actualException);
        }

        throw new AssertionError(String.format(
                "Expected %s to be thrown, but nothing was thrown.", expectedException.getName()));
    }

    /**
     * Represents a function which does not return anything and may throw an exception.
     */
    @FunctionalInterface
    public interface VoidCallable {
        void call() throws Exception;
    }

}

package seedu.address.testutil;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.function.Executable;

/**
 * A set of assertion methods useful for writing tests.
 */
public class Assert {

    /**
     * Asserts that the {@code executable} throws the {@code expectedType} Exception.
     * This is a wrapper method that invokes {@link Assertions#assertThrows(Class, Executable)}, to maintain consistency
     * with our custom {@link #assertThrows(Class, String, Executable)} method.
     * To standardize API calls in this project, users should use this method instead of
     * {@link Assertions#assertThrows(Class, Executable)}.
     */
    public static void assertThrows(Class<? extends Throwable> expectedType, Executable executable) {
        Assertions.assertThrows(expectedType, executable);
    }

    /**
     * Asserts that the {@code executable} throws the {@code expectedType} Exception with the {@code expectedMessage}.
     * If there's no need for the verification of the exception's error message, call
     * {@link #assertThrows(Class, Executable)} instead.
     *
     * @see #assertThrows(Class, Executable)
     */
    public static void assertThrows(Class<? extends Throwable> expectedType, String expectedMessage,
            Executable executable) {
        Throwable thrownException = Assertions.assertThrows(expectedType, executable);
        Assertions.assertEquals(expectedMessage, thrownException.getMessage());
    }
}

Use JUnit 5 Assert#assertThrows