diff --git a/CHANGES.md b/CHANGES.md index f638223d..851be4dc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,11 +18,18 @@ these methods will need to be updated. The following methods are affected: * `org.hamcrest.core.IsIterableContaining.hasItems` * TODO: decide if these breaking changes should trigger a major version upgrade (i.e v4.0) -### Improvements +### New Features -* Javadoc improvements and cleanup ([PR #420](https://github.com/hamcrest/JavaHamcrest/pull/420)) * Optional matchers ([PR #421](https://github.com/hamcrest/JavaHamcrest/pull/421)) +* Matchers for thrown exceptions ([PR #423](https://github.com/hamcrest/JavaHamcrest/pull/423)) + +### Improvements + +* Javadoc improvements and cleanup ([PR #420](https://github.com/hamcrest/JavaHamcrest/pull/420), +[#427](https://github.com/hamcrest/JavaHamcrest/issues/427), +[PR #428](https://github.com/hamcrest/JavaHamcrest/pull/428)) * Derive version from git tags ([PR #419](https://github.com/hamcrest/JavaHamcrest/pull/419)) +* Migrate all tests to JUnit Jupiter ([PR #424](https://github.com/hamcrest/JavaHamcrest/pull/424)) ### Bugfixes diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..40e1c448 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,7 @@ +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +junit-jupiter = "5.11.3" + +[libraries] +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/hamcrest/hamcrest.gradle b/hamcrest/hamcrest.gradle index 5799a43e..5259c441 100644 --- a/hamcrest/hamcrest.gradle +++ b/hamcrest/hamcrest.gradle @@ -5,13 +5,12 @@ plugins { version = rootProject.version dependencies { - testImplementation(group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.10.3') { - transitive = true - } - testImplementation(group: 'org.junit.vintage', name: 'junit-vintage-engine', version: '5.10.3') { - transitive = true - exclude(module: 'hamcrest-core') - } + testImplementation libs.junit.jupiter + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +tasks.named("test") { + useJUnitPlatform() } jar { diff --git a/hamcrest/src/main/java/org/hamcrest/Condition.java b/hamcrest/src/main/java/org/hamcrest/Condition.java index 85293eb1..8518ec6c 100644 --- a/hamcrest/src/main/java/org/hamcrest/Condition.java +++ b/hamcrest/src/main/java/org/hamcrest/Condition.java @@ -19,6 +19,7 @@ public abstract class Condition { * @param the initial value type * @param the next step value type */ + @FunctionalInterface public interface Step { /** * Apply this condition to a value diff --git a/hamcrest/src/main/java/org/hamcrest/CoreMatchers.java b/hamcrest/src/main/java/org/hamcrest/CoreMatchers.java index 6dadcc52..f00a6e54 100644 --- a/hamcrest/src/main/java/org/hamcrest/CoreMatchers.java +++ b/hamcrest/src/main/java/org/hamcrest/CoreMatchers.java @@ -13,7 +13,10 @@ @SuppressWarnings("UnusedDeclaration") public class CoreMatchers { - private CoreMatchers() { + /** + * Unused + */ + public CoreMatchers() { } /** diff --git a/hamcrest/src/main/java/org/hamcrest/MatcherAssert.java b/hamcrest/src/main/java/org/hamcrest/MatcherAssert.java index 574f4f61..29d3364e 100644 --- a/hamcrest/src/main/java/org/hamcrest/MatcherAssert.java +++ b/hamcrest/src/main/java/org/hamcrest/MatcherAssert.java @@ -6,7 +6,10 @@ */ public class MatcherAssert { - private MatcherAssert() { + /** + * Unused. + */ + public MatcherAssert() { } /** diff --git a/hamcrest/src/main/java/org/hamcrest/Matchers.java b/hamcrest/src/main/java/org/hamcrest/Matchers.java index d5260fb1..60d8256a 100644 --- a/hamcrest/src/main/java/org/hamcrest/Matchers.java +++ b/hamcrest/src/main/java/org/hamcrest/Matchers.java @@ -3,6 +3,7 @@ import org.hamcrest.collection.ArrayMatching; import org.hamcrest.core.IsIterableContaining; import org.hamcrest.core.StringRegularExpression; +import org.hamcrest.exception.ThrowsException; import org.hamcrest.optional.OptionalEmpty; import org.hamcrest.optional.OptionalWithValue; import org.hamcrest.text.IsEqualCompressingWhiteSpace; @@ -21,7 +22,10 @@ @SuppressWarnings({"unused", "WeakerAccess"}) public class Matchers { - private Matchers() { + /** + * Unused + */ + public Matchers() { } /** @@ -1972,21 +1976,21 @@ public static Matcher hasLength(org.hamcrest.Matcherargument. - * For example: - * - *
-     * assertThat("text", length(4))
-     * 
- * - * @param length the expected length of the string - * @return The matcher. - */ - public static Matcher hasLength(int length) { - return org.hamcrest.text.CharSequenceLength.hasLength(length); - } + /** + * Creates a matcher of {@link CharSequence} that matches when a char sequence has the length + * of the specified argument. + * For example: + * + *
+   * assertThat("text", length(4))
+   * 
+ * + * @param length the expected length of the string + * @return The matcher. + */ + public static Matcher hasLength(int length) { + return org.hamcrest.text.CharSequenceLength.hasLength(length); + } /** * Creates a matcher that matches any examined object whose toString method @@ -2228,4 +2232,86 @@ public static Matcher> optionalWithValue(T value) { public static Matcher> optionalWithValue(Matcher matcher) { return OptionalWithValue.optionalWithValue(matcher); } + + /** + * Matcher for {@link Runnable} that expects an exception to be thrown + * + * @param type of the Runnable + * @return The matcher. + */ + public static Matcher throwsException() { + return ThrowsException.throwsException(); + } + + /** + * Matcher for {@link Throwable} that expects that the Runnable throws an exception equal to the provided throwable + * + * @param type of the Runnable + * @param type of the Throwable + * @param throwable the Throwable class against which examined exceptions are compared + * @return The matcher. + */ + public static Matcher throwsException(U throwable) { + return ThrowsException.throwsException(throwable); + } + + /** + * Matcher for {@link Throwable} that expects that the Runnable throws an exception of the provided throwableClass class + * + * @param type of the Runnable + * @param type of the Throwable + * @param throwableClass the Throwable class against which examined exceptions are compared + * @return The matcher. + */ + public static Matcher throwsException(Class throwableClass) { + return ThrowsException.throwsException(throwableClass); + } + + /** + * Matcher for {@link Throwable} that expects that the Runnable throws an exception of the provided throwableClass class and has a message equal to the provided message + * + * @param type of the Runnable + * @param type of the Throwable + * @param throwableClass the Throwable class against which examined exceptions are compared + * @param message the String against which examined exception messages are compared + * @return The matcher. + */ + public static Matcher throwsException(Class throwableClass, String message) { + return ThrowsException.throwsException(throwableClass, message); + } + + /** + * Matcher for {@link Throwable} that expects that the Runnable throws an exception of the provided throwableClass class and has a message matching the provided messageMatcher + * + * @param type of the Runnable + * @param type of the Throwable + * @param throwableClass the Throwable class against which examined exceptions are compared + * @param messageMatcher matcher to validate exception's message + * @return The matcher. + */ + public static Matcher throwsException(Class throwableClass, Matcher messageMatcher) { + return ThrowsException.throwsException(throwableClass, messageMatcher); + } + + /** + * Matcher for {@link Throwable} that expects that the Runnable throws an exception with a message equal to the provided message + * + * @param type of the Runnable + * @param message the String against which examined exception messages are compared + * @return The matcher. + */ + public static Matcher throwsExceptionWithMessage(String message) { + return ThrowsException.throwsExceptionWithMessage(message); + } + + /** + * Matcher for {@link Throwable} that expects that the Runnable throws an exception with a message matching the provided messageMatcher + * + * @param type of the Runnable + * @param messageMatcher matcher to validate exception's message + * @return The matcher. + */ + public static Matcher throwsExceptionWithMessage(Matcher messageMatcher) { + return ThrowsException.throwsExceptionWithMessage(messageMatcher); + } } diff --git a/hamcrest/src/main/java/org/hamcrest/beans/HasProperty.java b/hamcrest/src/main/java/org/hamcrest/beans/HasProperty.java index cce9a8b3..8494c9a3 100644 --- a/hamcrest/src/main/java/org/hamcrest/beans/HasProperty.java +++ b/hamcrest/src/main/java/org/hamcrest/beans/HasProperty.java @@ -3,7 +3,6 @@ import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; -import org.hamcrest.collection.ArrayMatching; /** * A matcher that checks if an object has a JavaBean property with the @@ -31,7 +30,8 @@ public HasProperty(String propertyName) { @Override public boolean matchesSafely(T obj) { try { - return PropertyUtil.getPropertyDescriptor(propertyName, obj) != null; + return PropertyUtil.getPropertyDescriptor(propertyName, obj) != null || + PropertyUtil.getMethodDescriptor(propertyName, obj) != null; } catch (IllegalArgumentException e) { return false; } diff --git a/hamcrest/src/main/java/org/hamcrest/beans/HasPropertyWithValue.java b/hamcrest/src/main/java/org/hamcrest/beans/HasPropertyWithValue.java index a734f800..f45c1264 100644 --- a/hamcrest/src/main/java/org/hamcrest/beans/HasPropertyWithValue.java +++ b/hamcrest/src/main/java/org/hamcrest/beans/HasPropertyWithValue.java @@ -5,6 +5,8 @@ import org.hamcrest.Matcher; import org.hamcrest.TypeSafeDiagnosingMatcher; +import java.beans.FeatureDescriptor; +import java.beans.MethodDescriptor; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -26,7 +28,7 @@ *

Example Usage

* Consider the situation where we have a class representing a person, which * follows the basic JavaBean convention of having get() and possibly set() - * methods for it's properties: + * methods for its properties: *
{@code  public class Person {
  *   private String name;
  *   public Person(String person) {
@@ -69,7 +71,7 @@
  */
 public class HasPropertyWithValue extends TypeSafeDiagnosingMatcher {
 
-    private static final Condition.Step WITH_READ_METHOD = withReadMethod();
+    private static final Condition.Step WITH_READ_METHOD = withReadMethod();
     private final String propertyName;
     private final Matcher valueMatcher;
     private final String messageFormat;
@@ -111,8 +113,11 @@ public void describeTo(Description description) {
                    .appendDescriptionOf(valueMatcher).appendText(")");
     }
 
-    private Condition propertyOn(T bean, Description mismatch) {
-        PropertyDescriptor property = PropertyUtil.getPropertyDescriptor(propertyName, bean);
+    private Condition propertyOn(T bean, Description mismatch) {
+        FeatureDescriptor property = PropertyUtil.getPropertyDescriptor(propertyName, bean);
+        if (property == null) {
+            property = PropertyUtil.getMethodDescriptor(propertyName, bean);
+        }
         if (property == null) {
             mismatch.appendText("No property \"" + propertyName + "\"");
             return notMatched();
@@ -122,22 +127,19 @@ private Condition propertyOn(T bean, Description mismatch) {
     }
 
     private Condition.Step withPropertyValue(final T bean) {
-        return new Condition.Step() {
-            @Override
-            public Condition apply(Method readMethod, Description mismatch) {
-                try {
-                    return matched(readMethod.invoke(bean, NO_ARGUMENTS), mismatch);
-                } catch (InvocationTargetException e) {
-                    mismatch
-                      .appendText("Calling '")
-                      .appendText(readMethod.toString())
-                      .appendText("': ")
-                      .appendValue(e.getTargetException().getMessage());
-                    return notMatched();
-                } catch (Exception e) {
-                    throw new IllegalStateException(
-                      "Calling: '" + readMethod + "' should not have thrown " + e);
-                }
+        return (readMethod, mismatch) -> {
+            try {
+                return matched(readMethod.invoke(bean, NO_ARGUMENTS), mismatch);
+            } catch (InvocationTargetException e) {
+                mismatch
+                  .appendText("Calling '")
+                  .appendText(readMethod.toString())
+                  .appendText("': ")
+                  .appendValue(e.getTargetException().getMessage());
+                return notMatched();
+            } catch (Exception e) {
+                throw new IllegalStateException(
+                  "Calling: '" + readMethod + "' should not have thrown " + e);
             }
         };
     }
@@ -147,17 +149,16 @@ private static Matcher nastyGenericsWorkaround(Matcher valueMatcher)
         return (Matcher) valueMatcher;
     }
 
-    private static Condition.Step withReadMethod() {
-        return new Condition.Step() {
-            @Override
-            public Condition apply(PropertyDescriptor property, Description mismatch) {
-                final Method readMethod = property.getReadMethod();
-                if (null == readMethod) {
-                    mismatch.appendText("property \"" + property.getName() + "\" is not readable");
-                    return notMatched();
-                }
-                return matched(readMethod, mismatch);
+    private static Condition.Step withReadMethod() {
+        return (property, mismatch) -> {
+            final Method readMethod = property instanceof PropertyDescriptor ?
+                    ((PropertyDescriptor) property).getReadMethod() :
+                    (((MethodDescriptor) property).getMethod());
+            if (null == readMethod || readMethod.getReturnType() == void.class) {
+                mismatch.appendText("property \"" + property.getName() + "\" is not readable");
+                return notMatched();
             }
+            return matched(readMethod, mismatch);
         };
     }
 
diff --git a/hamcrest/src/main/java/org/hamcrest/beans/PropertyUtil.java b/hamcrest/src/main/java/org/hamcrest/beans/PropertyUtil.java
index 71b7dcea..40d5a37d 100644
--- a/hamcrest/src/main/java/org/hamcrest/beans/PropertyUtil.java
+++ b/hamcrest/src/main/java/org/hamcrest/beans/PropertyUtil.java
@@ -2,7 +2,12 @@
 
 import java.beans.IntrospectionException;
 import java.beans.Introspector;
+import java.beans.MethodDescriptor;
 import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Utility class with static methods for accessing properties on JavaBean objects.
@@ -11,6 +16,7 @@
  *
  * @author Iain McGinniss
  * @author Steve Freeman
+ * @author Uno Kim
  * @since 1.1.0
  */
 public class PropertyUtil {
@@ -27,7 +33,7 @@ private PropertyUtil() {
      * @param fromObj
      *     the object to check.
      * @return the descriptor of the property, or null if the property does not exist.
-     * @throws IllegalArgumentException if there's a introspection failure
+     * @throws IllegalArgumentException if there's an introspection failure
      */
     public static PropertyDescriptor getPropertyDescriptor(String propertyName, Object fromObj) throws IllegalArgumentException {
         for (PropertyDescriptor property : propertyDescriptorsFor(fromObj, null)) {
@@ -45,7 +51,7 @@ public static PropertyDescriptor getPropertyDescriptor(String propertyName, Obje
      * @param fromObj Use the class of this object
      * @param stopClass Don't include any properties from this ancestor class upwards.
      * @return Property descriptors
-     * @throws IllegalArgumentException if there's a introspection failure
+     * @throws IllegalArgumentException if there's an introspection failure
      */
     public static PropertyDescriptor[] propertyDescriptorsFor(Object fromObj, Class stopClass) throws IllegalArgumentException {
       try {
@@ -55,6 +61,73 @@ public static PropertyDescriptor[] propertyDescriptorsFor(Object fromObj, Class<
       }
     }
 
+    /**
+     * Returns the description of the read accessor method with the provided
+     * name on the provided object's interface.
+     * This is what you need when you try to find a property from a target object
+     * when it doesn't follow standard JavaBean specification, a Java Record for example.
+     *
+     * @param propertyName the object property name.
+     * @param fromObj the object to check.
+     * @return the descriptor of the method, or null if the method does not exist.
+     * @throws IllegalArgumentException if there's an introspection failure
+     * @see Java Records
+     *
+     */
+    public static MethodDescriptor getMethodDescriptor(String propertyName, Object fromObj) throws IllegalArgumentException {
+        for (MethodDescriptor method : recordReadAccessorMethodDescriptorsFor(fromObj, null)) {
+            if (method.getName().equals(propertyName)) {
+                return method;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns read accessor method descriptors for the class associated with the given object.
+     * This is useful when you find getter methods for the fields from the object
+     * when it doesn't follow standard JavaBean specification, a Java Record for example.
+     * Be careful as this doesn't return standard JavaBean getter methods, like a method starting with {@code get-}.
+     *
+     * @param fromObj Use the class of this object
+     * @param stopClass Don't include any properties from this ancestor class upwards.
+     * @return Method descriptors for read accessor methods
+     * @throws IllegalArgumentException if there's an introspection failure
+     */
+    public static MethodDescriptor[] recordReadAccessorMethodDescriptorsFor(Object fromObj, Class stopClass) throws IllegalArgumentException {
+        try {
+            Set recordComponentNames = getFieldNames(fromObj);
+            MethodDescriptor[] methodDescriptors = Introspector.getBeanInfo(fromObj.getClass(), stopClass).getMethodDescriptors();
+
+            return Arrays.stream(methodDescriptors)
+                    .filter(x -> recordComponentNames.contains(x.getDisplayName()))
+                    .filter(x -> x.getMethod().getReturnType() != void.class)
+                    .filter(x -> x.getMethod().getParameterCount() == 0)
+                    .toArray(MethodDescriptor[]::new);
+        } catch (IntrospectionException e) {
+            throw new IllegalArgumentException("Could not get method descriptors for " + fromObj.getClass(), e);
+        }
+    }
+
+    /**
+     * Returns the field names of the given object.
+     * It can be the names of the record components of Java Records, for example.
+     *
+     * @param fromObj the object to check
+     * @return The field names
+     * @throws IllegalArgumentException if there's a security issue reading the fields
+     */
+    public static Set getFieldNames(Object fromObj) throws IllegalArgumentException {
+        try {
+            return Arrays.stream(fromObj.getClass().getDeclaredFields())
+                    .map(Field::getName)
+                    .collect(Collectors.toSet());
+        } catch (SecurityException e) {
+            throw new IllegalArgumentException("Could not get record component names for " + fromObj.getClass(), e);
+        }
+    }
+
     /**
      * Empty object array, used for documenting that we are deliberately passing no arguments to a method.
      */
diff --git a/hamcrest/src/main/java/org/hamcrest/beans/SamePropertyValuesAs.java b/hamcrest/src/main/java/org/hamcrest/beans/SamePropertyValuesAs.java
index 94f4dba1..fbb0175f 100644
--- a/hamcrest/src/main/java/org/hamcrest/beans/SamePropertyValuesAs.java
+++ b/hamcrest/src/main/java/org/hamcrest/beans/SamePropertyValuesAs.java
@@ -4,6 +4,8 @@
 import org.hamcrest.DiagnosingMatcher;
 import org.hamcrest.Matcher;
 
+import java.beans.FeatureDescriptor;
+import java.beans.MethodDescriptor;
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.Method;
 import java.util.*;
@@ -11,6 +13,7 @@
 import static java.util.Arrays.asList;
 import static org.hamcrest.beans.PropertyUtil.NO_ARGUMENTS;
 import static org.hamcrest.beans.PropertyUtil.propertyDescriptorsFor;
+import static org.hamcrest.beans.PropertyUtil.recordReadAccessorMethodDescriptorsFor;
 import static org.hamcrest.core.IsEqual.equalTo;
 
 /**
@@ -33,7 +36,11 @@ public class SamePropertyValuesAs extends DiagnosingMatcher {
      */
     @SuppressWarnings("WeakerAccess")
     public SamePropertyValuesAs(T expectedBean, List ignoredProperties) {
-        PropertyDescriptor[] descriptors = propertyDescriptorsFor(expectedBean, Object.class);
+        FeatureDescriptor[] descriptors = propertyDescriptorsFor(expectedBean, Object.class);
+        if (descriptors == null || descriptors.length == 0) {
+            descriptors = recordReadAccessorMethodDescriptorsFor(expectedBean, Object.class);
+        }
+
         this.expectedBean = expectedBean;
         this.ignoredFields = ignoredProperties;
         this.propertyNames = propertyNamesFrom(descriptors, ignoredProperties);
@@ -87,27 +94,27 @@ private boolean hasMatchingValues(Object actual, Description mismatchDescription
         return true;
     }
 
-    private static  List propertyMatchersFor(T bean, PropertyDescriptor[] descriptors, List ignoredFields) {
+    private static  List propertyMatchersFor(T bean, FeatureDescriptor[] descriptors, List ignoredFields) {
         List result = new ArrayList<>(descriptors.length);
-        for (PropertyDescriptor propertyDescriptor : descriptors) {
-            if (isIgnored(ignoredFields, propertyDescriptor)) {
-                result.add(new PropertyMatcher(propertyDescriptor, bean));
+        for (FeatureDescriptor descriptor : descriptors) {
+            if (isNotIgnored(ignoredFields, descriptor)) {
+                result.add(new PropertyMatcher(descriptor, bean));
             }
         }
         return result;
     }
 
-    private static Set propertyNamesFrom(PropertyDescriptor[] descriptors, List ignoredFields) {
+    private static Set propertyNamesFrom(FeatureDescriptor[] descriptors, List ignoredFields) {
         HashSet result = new HashSet<>();
-        for (PropertyDescriptor propertyDescriptor : descriptors) {
-            if (isIgnored(ignoredFields, propertyDescriptor)) {
-                result.add(propertyDescriptor.getDisplayName());
+        for (FeatureDescriptor descriptor : descriptors) {
+            if (isNotIgnored(ignoredFields, descriptor)) {
+                result.add(descriptor.getDisplayName());
             }
         }
         return result;
     }
 
-    private static boolean isIgnored(List ignoredFields, PropertyDescriptor propertyDescriptor) {
+    private static boolean isNotIgnored(List ignoredFields, FeatureDescriptor propertyDescriptor) {
         return ! ignoredFields.contains(propertyDescriptor.getDisplayName());
     }
 
@@ -117,9 +124,11 @@ private static class PropertyMatcher extends DiagnosingMatcher {
         private final Matcher matcher;
         private final String propertyName;
 
-        public PropertyMatcher(PropertyDescriptor descriptor, Object expectedObject) {
+        public PropertyMatcher(FeatureDescriptor descriptor, Object expectedObject) {
             this.propertyName = descriptor.getDisplayName();
-            this.readMethod = descriptor.getReadMethod();
+            this.readMethod = descriptor instanceof PropertyDescriptor ?
+                    ((PropertyDescriptor) descriptor).getReadMethod() :
+                    ((MethodDescriptor) descriptor).getMethod();
             this.matcher = equalTo(readProperty(readMethod, expectedObject));
         }
 
diff --git a/hamcrest/src/main/java/org/hamcrest/exception/ThrowsException.java b/hamcrest/src/main/java/org/hamcrest/exception/ThrowsException.java
new file mode 100644
index 00000000..aebb96b7
--- /dev/null
+++ b/hamcrest/src/main/java/org/hamcrest/exception/ThrowsException.java
@@ -0,0 +1,149 @@
+package org.hamcrest.exception;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.hamcrest.core.IsInstanceOf;
+
+import static org.hamcrest.core.IsAnything.anything;
+import static org.hamcrest.core.IsEqual.equalTo;
+
+/**
+ * Tests if a Runnable throws a matching exception.
+ *
+ * @param  the type of the matched Runnable
+ */
+public class ThrowsException extends TypeSafeDiagnosingMatcher {
+  private final IsInstanceOf classMatcher;
+  private final Matcher messageMatcher;
+
+  /**
+   * Constructor, best called from one of the static {@link #throwsException()} methods.
+   * @param classMatcher the matcher for the type of the exception
+   * @param messageMatcher the matcher for the exception message
+   */
+  public ThrowsException(IsInstanceOf classMatcher, Matcher messageMatcher) {
+    this.classMatcher = classMatcher;
+    this.messageMatcher = messageMatcher;
+  }
+
+  /**
+   * Matcher for {@link Runnable} that expects an exception to be thrown
+   *
+   * @param  type of the Runnable
+   * @return The matcher.
+   */
+  public static  Matcher throwsException() {
+    return throwsException(Throwable.class);
+  }
+
+  /**
+   * Matcher for {@link Throwable} that expects that the Runnable throws an exception equal
+   * to the provided throwable
+   *
+   * @param        type of the Runnable
+   * @param        type of the Throwable
+   * @param throwable the Throwable class against which examined exceptions are compared
+   * @return The matcher.
+   */
+  public static  Matcher throwsException(U throwable) {
+    return throwsException(throwable.getClass(), throwable.getMessage());
+  }
+
+  /**
+   * Matcher for {@link Throwable} that expects that the Runnable throws an exception of the
+   * provided throwableClass class
+   *
+   * @param             type of the Runnable
+   * @param             type of the Throwable
+   * @param throwableClass the Throwable class against which examined exceptions are compared
+   * @return The matcher.
+   */
+  public static  Matcher throwsException(Class throwableClass) {
+    return new ThrowsException<>(new IsInstanceOf(throwableClass), anything(""));
+  }
+
+  /**
+   * Matcher for {@link Throwable} that expects that the Runnable throws an exception of the
+   * provided throwableClass class and has a message equal to the provided
+   * message
+   *
+   * @param             type of the Runnable
+   * @param             type of the Throwable
+   * @param throwableClass the Throwable class against which examined exceptions are compared
+   * @param exactMessage   the String against which examined exception messages are compared
+   * @return The matcher.
+   */
+  public static  Matcher throwsException(Class throwableClass, String exactMessage) {
+    return throwsException(throwableClass, equalTo(exactMessage));
+  }
+
+  /**
+   * Matcher for {@link Throwable} that expects that the Runnable throws an exception of the provided
+   * throwableClass class and has a message matching the provided
+   * messageMatcher
+   *
+   * @param             type of the Runnable
+   * @param             type of the Throwable
+   * @param throwableClass the Throwable class against which examined exceptions are compared
+   * @param messageMatcher matcher to validate exception's message
+   * @return The matcher.
+   */
+  public static  Matcher throwsException(Class throwableClass, Matcher messageMatcher) {
+    return new ThrowsException<>(new IsInstanceOf(throwableClass), messageMatcher);
+  }
+
+  /**
+   * Matcher for {@link Throwable} that expects that the Runnable throws an exception with a message equal to the provided message
+   *
+   * @param           type of the Runnable
+   * @param exactMessage the String against which examined exception messages are compared
+   * @return The matcher.
+   */
+  public static  Matcher throwsExceptionWithMessage(String exactMessage) {
+    return throwsException(Throwable.class, equalTo(exactMessage));
+  }
+
+  /**
+   * Matcher for {@link Throwable} that expects that the Runnable throws an exception with a message matching the provided messageMatcher
+   *
+   * @param             type of the Runnable
+   * @param messageMatcher matcher to validate exception's message
+   * @return The matcher.
+   */
+  public static  Matcher throwsExceptionWithMessage(Matcher messageMatcher) {
+    return throwsException(Throwable.class, messageMatcher);
+  }
+
+  @Override
+  protected boolean matchesSafely(T runnable, Description mismatchDescription) {
+    try {
+      runnable.run();
+      mismatchDescription.appendText("the runnable didn't throw");
+      return false;
+    } catch (Throwable t) {
+      boolean classMatches = classMatcher.matches(t);
+      if (!classMatches) {
+        mismatchDescription.appendText("thrown exception class was ").appendText(t.getClass().getName());
+      }
+
+      boolean messageMatches = messageMatcher.matches(t.getMessage());
+      if (!messageMatches) {
+        if (!classMatches) {
+          mismatchDescription.appendText(" and the ");
+        }
+        mismatchDescription.appendText("thrown exception message ");
+        messageMatcher.describeMismatch(t.getMessage(), mismatchDescription);
+      }
+
+      return classMatches && messageMatches;
+    }
+  }
+
+  @Override
+  public void describeTo(Description description) {
+    description
+        .appendText("a runnable throwing ").appendDescriptionOf(classMatcher)
+        .appendText(" with message ").appendDescriptionOf(messageMatcher);
+  }
+}
diff --git a/hamcrest/src/main/java/org/hamcrest/exception/package-info.java b/hamcrest/src/main/java/org/hamcrest/exception/package-info.java
new file mode 100644
index 00000000..8b85543e
--- /dev/null
+++ b/hamcrest/src/main/java/org/hamcrest/exception/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Matchers of exceptions.
+ */
+package org.hamcrest.exception;
diff --git a/hamcrest/src/test/java/org/hamcrest/AbstractMatcherTest.java b/hamcrest/src/test/java/org/hamcrest/AbstractMatcherTest.java
deleted file mode 100644
index e22e23e0..00000000
--- a/hamcrest/src/test/java/org/hamcrest/AbstractMatcherTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.hamcrest;
-
-import junit.framework.TestCase;
-import org.junit.Assert;
-
-public abstract class AbstractMatcherTest extends TestCase {
-
-  /**
-   * Create an instance of the Matcher so some generic safety-net tests can be run on it.
-   */
-  protected abstract Matcher createMatcher();
-
-  public static  void assertMatches(Matcher matcher, T arg) {
-      assertMatches("Expected match, but mismatched", matcher, arg);
-  }
-
-  public static  void assertMatches(String message, Matcher matcher, Object arg) {
-    if (!matcher.matches(arg)) {
-      Assert.fail(message + " because: '" + mismatchDescription(matcher, arg) + "'");
-    }
-  }
-
-  public static  void assertDoesNotMatch(Matcher c, T arg) {
-      assertDoesNotMatch("Unexpected match", c, arg);
-  }
-
-  public static  void assertDoesNotMatch(String message, Matcher c, T arg) {
-    Assert.assertFalse(message, c.matches(arg));
-  }
-
-  public static void assertDescription(String expected, Matcher matcher) {
-    Description description = new StringDescription();
-    description.appendDescriptionOf(matcher);
-    Assert.assertEquals("Expected description", expected, description.toString().trim());
-  }
-
-  public static  void assertMismatchDescription(String expected, Matcher matcher, Object arg) {
-    Assert.assertFalse("Precondition: Matcher should not match item.", matcher.matches(arg));
-    Assert.assertEquals("Expected mismatch description", expected, mismatchDescription(matcher, arg));
-  }
-
-  public static void assertNullSafe(Matcher matcher) {
-      try {
-          matcher.matches(null);
-      }
-      catch (Exception e) {
-          Assert.fail("Matcher was not null safe");
-      }
-  }
-
-  public static void assertUnknownTypeSafe(Matcher matcher) {
-      try {
-          matcher.matches(new UnknownType());
-      } catch (Exception e) {
-          Assert.fail("Matcher was not unknown type safe, because: " + e);
-      }
-  }
-
-  public void testIsNullSafe() {
-    assertNullSafe(createMatcher());
-  }
-
-  public void testCopesWithUnknownTypes() {
-    createMatcher().matches(new UnknownType());
-  }
-
-    private static  String mismatchDescription(Matcher matcher, Object arg) {
-      Description description = new StringDescription();
-      matcher.describeMismatch(arg, description);
-      return description.toString().trim();
-    }
-
-  @SuppressWarnings("WeakerAccess")
-  public static class UnknownType {
-  }
-
-}
diff --git a/hamcrest/src/test/java/org/hamcrest/BaseDescriptionTest.java b/hamcrest/src/test/java/org/hamcrest/BaseDescriptionTest.java
index 8dc77d4f..05ae5252 100644
--- a/hamcrest/src/test/java/org/hamcrest/BaseDescriptionTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/BaseDescriptionTest.java
@@ -1,8 +1,8 @@
 package org.hamcrest;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public final class BaseDescriptionTest {
 
diff --git a/hamcrest/src/test/java/org/hamcrest/BaseMatcherTest.java b/hamcrest/src/test/java/org/hamcrest/BaseMatcherTest.java
index 136fe9a1..c7d73cb6 100644
--- a/hamcrest/src/test/java/org/hamcrest/BaseMatcherTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/BaseMatcherTest.java
@@ -1,8 +1,8 @@
 package org.hamcrest;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public final class BaseMatcherTest {
 
diff --git a/hamcrest/src/test/java/org/hamcrest/CustomMatcherTest.java b/hamcrest/src/test/java/org/hamcrest/CustomMatcherTest.java
index 43cf58b0..736a58c3 100644
--- a/hamcrest/src/test/java/org/hamcrest/CustomMatcherTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/CustomMatcherTest.java
@@ -1,8 +1,9 @@
 package org.hamcrest;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.test.MatcherAssertions.assertDescription;
 
-import static org.hamcrest.AbstractMatcherTest.assertDescription;
 
 public final class CustomMatcherTest {
 
diff --git a/hamcrest/src/test/java/org/hamcrest/CustomTypeSafeMatcherTest.java b/hamcrest/src/test/java/org/hamcrest/CustomTypeSafeMatcherTest.java
index 3f71115a..5820fd88 100644
--- a/hamcrest/src/test/java/org/hamcrest/CustomTypeSafeMatcherTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/CustomTypeSafeMatcherTest.java
@@ -1,8 +1,8 @@
 package org.hamcrest;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 
 public final class CustomTypeSafeMatcherTest {
 
diff --git a/hamcrest/src/test/java/org/hamcrest/FeatureMatcherTest.java b/hamcrest/src/test/java/org/hamcrest/FeatureMatcherTest.java
index 8248c997..82e89aa2 100644
--- a/hamcrest/src/test/java/org/hamcrest/FeatureMatcherTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/FeatureMatcherTest.java
@@ -1,10 +1,10 @@
 package org.hamcrest;
 
 import org.hamcrest.core.IsEqual;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
-import static org.junit.Assert.assertEquals;
+import static org.hamcrest.test.MatcherAssertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public final class FeatureMatcherTest {
 
diff --git a/hamcrest/src/test/java/org/hamcrest/MatcherAssertTest.java b/hamcrest/src/test/java/org/hamcrest/MatcherAssertTest.java
index 556e06bd..93da7636 100644
--- a/hamcrest/src/test/java/org/hamcrest/MatcherAssertTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/MatcherAssertTest.java
@@ -1,10 +1,10 @@
 package org.hamcrest;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public final class MatcherAssertTest {
 
diff --git a/hamcrest/src/test/java/org/hamcrest/NullDescriptionTest.java b/hamcrest/src/test/java/org/hamcrest/NullDescriptionTest.java
index cccece0a..dc5ac4ab 100644
--- a/hamcrest/src/test/java/org/hamcrest/NullDescriptionTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/NullDescriptionTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest;
 
 import org.hamcrest.Description.NullDescription;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public final class NullDescriptionTest {
 
diff --git a/hamcrest/src/test/java/org/hamcrest/TypeSafeDiagnosingMatcherTest.java b/hamcrest/src/test/java/org/hamcrest/TypeSafeDiagnosingMatcherTest.java
index 0b001391..61062074 100644
--- a/hamcrest/src/test/java/org/hamcrest/TypeSafeDiagnosingMatcherTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/TypeSafeDiagnosingMatcherTest.java
@@ -1,8 +1,8 @@
 package org.hamcrest;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 
 /**
  * @author Steve Freeman 2016 http://www.hamcrest.com
diff --git a/hamcrest/src/test/java/org/hamcrest/TypeSafeMatcherTest.java b/hamcrest/src/test/java/org/hamcrest/TypeSafeMatcherTest.java
index 0a9d7432..6fa6caef 100644
--- a/hamcrest/src/test/java/org/hamcrest/TypeSafeMatcherTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/TypeSafeMatcherTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.assertMismatchDescription;
-import static org.junit.Assert.assertFalse;
+import static org.hamcrest.test.MatcherAssertions.assertMismatchDescription;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 
 @SuppressWarnings("WeakerAccess")
 public final class TypeSafeMatcherTest {
diff --git a/hamcrest/src/test/java/org/hamcrest/beans/HasPropertyTest.java b/hamcrest/src/test/java/org/hamcrest/beans/HasPropertyTest.java
index 5a7ed7b9..9a5b06d4 100644
--- a/hamcrest/src/test/java/org/hamcrest/beans/HasPropertyTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/beans/HasPropertyTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.beans;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.beans.HasProperty.hasProperty;
 
 /**
@@ -16,6 +16,7 @@
 public final class HasPropertyTest {
 
     private final HasPropertyWithValueTest.BeanWithoutInfo bean = new HasPropertyWithValueTest.BeanWithoutInfo("a bean", false);
+    private final HasPropertyWithValueTest.RecordLikeBeanWithoutInfo record = new HasPropertyWithValueTest.RecordLikeBeanWithoutInfo("a record", false);
 
     @Test public void
     copesWithNullsAndUnknownTypes() {
@@ -28,11 +29,14 @@ public final class HasPropertyTest {
     @Test public void
     matchesWhenThePropertyExists() {
         assertMatches(hasProperty("writeOnlyProperty"), bean);
+        assertMatches(hasProperty("property"), record);
     }
 
     @Test public void
     doesNotMatchIfPropertyDoesNotExist() {
         assertDoesNotMatch(hasProperty("aNonExistentProp"), bean);
+        assertDoesNotMatch(hasProperty("aNonExistentProp"), record);
+        assertDoesNotMatch(hasProperty("notAGetterMethod"), record);
     }
 
     @Test public void
@@ -44,6 +48,8 @@ public final class HasPropertyTest {
     describesAMismatch() {
         assertMismatchDescription("no \"aNonExistentProp\" in <[Person: a bean]>",
                                   hasProperty("aNonExistentProp"), bean);
+        assertMismatchDescription("no \"aNonExistentProp\" in <[Person: a record]>",
+                                  hasProperty("aNonExistentProp"), record);
     }
 
 }
diff --git a/hamcrest/src/test/java/org/hamcrest/beans/HasPropertyWithValueTest.java b/hamcrest/src/test/java/org/hamcrest/beans/HasPropertyWithValueTest.java
index bef5ac1e..a7dcc00d 100644
--- a/hamcrest/src/test/java/org/hamcrest/beans/HasPropertyWithValueTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/beans/HasPropertyWithValueTest.java
@@ -2,17 +2,22 @@
 
 import org.hamcrest.*;
 import org.hamcrest.core.IsEqual;
+import org.hamcrest.test.AbstractMatcherTest;
+import org.junit.jupiter.api.Test;
 
 import java.beans.IntrospectionException;
 import java.beans.PropertyDescriptor;
 import java.beans.SimpleBeanInfo;
+import java.util.Objects;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
 import static org.hamcrest.beans.HasPropertyWithValue.hasPropertyAtPath;
 import static org.hamcrest.core.IsAnything.anything;
 import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
  * @author Iain McGinniss
@@ -26,6 +31,9 @@ public class HasPropertyWithValueTest extends AbstractMatcherTest {
   private final BeanWithoutInfo shouldMatch = new BeanWithoutInfo("is expected", true);
   private final BeanWithoutInfo shouldNotMatch = new BeanWithoutInfo("not expected", false);
 
+  private final RecordLikeBeanWithoutInfo recordShouldMatch = new RecordLikeBeanWithoutInfo("is expected", true);
+  private final RecordLikeBeanWithoutInfo recordShouldNotMatch = new RecordLikeBeanWithoutInfo("not expected", false);
+
   private final BeanWithInfo beanWithInfo = new BeanWithInfo("with info");
 
   @Override
@@ -33,6 +41,7 @@ protected Matcher createMatcher() {
     return hasProperty("irrelevant", anything());
   }
 
+  @Test
   public void testMatchesBeanWithoutInfoWithMatchedNamedProperty() {
     final Matcher propertyMatcher = hasProperty("property", equalTo("is expected"));
 
@@ -40,6 +49,15 @@ public void testMatchesBeanWithoutInfoWithMatchedNamedProperty() {
     assertMismatchDescription("property 'property' was \"not expected\"", propertyMatcher, shouldNotMatch);
   }
 
+  @Test
+  public void testMatchesRecordLikeBeanWithoutInfoWithMatchedNamedProperty() {
+    final Matcher propertyMatcher = hasProperty("property", equalTo("is expected"));
+
+    assertMatches("with property", propertyMatcher, recordShouldMatch);
+    assertMismatchDescription("property 'property' was \"not expected\"", propertyMatcher, recordShouldNotMatch);
+  }
+
+  @Test
   public void testMatchesBeanWithoutInfoWithMatchedNamedBooleanProperty() {
     final Matcher booleanPropertyMatcher = hasProperty("booleanProperty", is(true));
 
@@ -47,22 +65,40 @@ public void testMatchesBeanWithoutInfoWithMatchedNamedBooleanProperty() {
     assertMismatchDescription("property 'booleanProperty' was ", booleanPropertyMatcher, shouldNotMatch);
   }
 
+  @Test
+  public void testMatchesRecordLikeBeanWithoutInfoWithMatchedNamedBooleanProperty() {
+    final Matcher booleanPropertyMatcher = hasProperty("booleanProperty", is(true));
+
+    assertMatches("with property", booleanPropertyMatcher, recordShouldMatch);
+    assertMismatchDescription("property 'booleanProperty' was ", booleanPropertyMatcher, recordShouldNotMatch);
+  }
+
+  @Test
   public void testMatchesBeanWithInfoWithMatchedNamedProperty() {
     assertMatches("with bean info", hasProperty("property", equalTo("with info")), beanWithInfo);
     assertMismatchDescription("property 'property' was \"with info\"",
         hasProperty("property", equalTo("without info")), beanWithInfo);
   }
 
+  @Test
   public void testDoesNotMatchBeanWithoutInfoOrMatchedNamedProperty() {
     assertMismatchDescription("No property \"nonExistentProperty\"",
                               hasProperty("nonExistentProperty", anything()), shouldNotMatch);
-   }
+  }
+
+  @Test
+  public void testDoesNotMatchRecordLikeBeanWithoutInfoOrMatchedNamedProperty() {
+    assertMismatchDescription("No property \"nonExistentProperty\"",
+                              hasProperty("nonExistentProperty", anything()), recordShouldNotMatch);
+  }
 
+  @Test
   public void testDoesNotMatchWriteOnlyProperty() {
     assertMismatchDescription("property \"writeOnlyProperty\" is not readable",
                               hasProperty("writeOnlyProperty", anything()), shouldNotMatch);
   }
 
+  @Test
   public void testMatchesPath() {
     assertMatches("1-step path", hasPropertyAtPath("property", equalTo("is expected")), shouldMatch);
     assertMatches("2-step path", hasPropertyAtPath("inner.property", equalTo("is expected")), new BeanWithInner(shouldMatch));
@@ -72,24 +108,44 @@ public void testMatchesPath() {
     assertMismatchDescription("inner.inner.property.was \"not expected\"", hasPropertyAtPath("inner.inner.property", equalTo("something")), new BeanWithInner(new BeanWithInner(shouldNotMatch)));
   }
 
+  @Test
+  public void testMatchesPathForJavaRecords() {
+    assertMatches("1-step path", hasPropertyAtPath("property", equalTo("is expected")), recordShouldMatch);
+    assertMatches("2-step path", hasPropertyAtPath("inner.property", equalTo("is expected")), new BeanWithInner(recordShouldMatch));
+    assertMatches("3-step path", hasPropertyAtPath("inner.inner.property", equalTo("is expected")), new BeanWithInner(new BeanWithInner(recordShouldMatch)));
+
+    assertMismatchDescription("inner.No property \"wrong\"", hasPropertyAtPath("inner.wrong.property", anything()), new BeanWithInner(new BeanWithInner(recordShouldMatch)));
+    assertMismatchDescription("inner.inner.property.was \"not expected\"", hasPropertyAtPath("inner.inner.property", equalTo("something")), new BeanWithInner(new BeanWithInner(recordShouldNotMatch)));
+  }
+
+  @Test
   public void testDescribeTo() {
     assertDescription("hasProperty(\"property\", )", hasProperty("property", equalTo(true)));
   }
 
+  @Test
   public void testMatchesPropertyAndValue() {
     assertMatches("property with value", hasProperty("property", anything()), beanWithInfo);
   }
 
+  @Test
+  public void testMatchesPropertyAndValueWithJavaRecords() {
+    assertMatches("property with value", hasProperty("property", anything()), recordShouldMatch);
+  }
+
+  @Test
   public void testDoesNotWriteMismatchIfPropertyMatches() {
     Description description = new StringDescription();
     hasProperty( "property", anything()).describeMismatch(beanWithInfo, description);
-    assertEquals("Expected mismatch description", "", description.toString());
+    assertEquals("", description.toString(), "Expected mismatch description");
   }
 
+  @Test
   public void testDescribesMissingPropertyMismatch() {
     assertMismatchDescription("No property \"honk\"", hasProperty("honk", anything()), shouldNotMatch);
   }
 
+  @Test
   public void testExceptionsInBeanMethodsShouldBeReportedCorrectly() {
     assertMismatchDescription(
       "Calling 'public java.lang.String org.hamcrest.beans.HasPropertyWithValueTest$BeanWithBug.getBroken()': \"bean failed\"",
@@ -97,6 +153,7 @@ public void testExceptionsInBeanMethodsShouldBeReportedCorrectly() {
       new BeanWithBug());
   }
 
+  @Test
   public void testCanAccessAnAnonymousInnerClass() {
     class X implements IX {
       @Override
@@ -141,6 +198,45 @@ public String toString() {
     }
   }
 
+  /**
+   * A Java Record-like class to test the functionality of
+   * {@link HasProperty}, {@link HasPropertyWithValue}
+   * with Java Records in JDK 8 environment.
+   *
+   * @see https://docs.oracle.com/en/java/javase/17/language/records.html
+   */
+  public static final class RecordLikeBeanWithoutInfo {
+    private final String property;
+    private final boolean booleanProperty;
+
+    public RecordLikeBeanWithoutInfo(String property, boolean booleanProperty) {
+      this.property = property;
+      this.booleanProperty = booleanProperty;
+    }
+
+    public String property() { return this.property; }
+    public boolean booleanProperty() { return this.booleanProperty; }
+    public void notAGetterMethod() {}
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (!(o instanceof RecordLikeBeanWithoutInfo)) return false;
+      RecordLikeBeanWithoutInfo that = (RecordLikeBeanWithoutInfo) o;
+      return Objects.equals(this.property, that.property) && this.booleanProperty == that.booleanProperty;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(property, booleanProperty);
+    }
+
+    @Override
+    public String toString() {
+      return "[Person: " + property + "]";
+    }
+  }
+
   @SuppressWarnings("WeakerAccess")
   public static class BeanWithInner {
     private final Object inner;
@@ -157,7 +253,7 @@ public static class BeanWithInfo {
     public String property() { return propertyValue; }
   }
 
-  public static class BeanWithInfoBeanInfo extends SimpleBeanInfo {
+  public static class BeanWithInfoBeanInfo extends SimpleBeanInfo { // TODO: No usage. Can be removed.
     @Override
     public PropertyDescriptor[] getPropertyDescriptors() {
       try {
diff --git a/hamcrest/src/test/java/org/hamcrest/beans/PropertyUtilTest.java b/hamcrest/src/test/java/org/hamcrest/beans/PropertyUtilTest.java
new file mode 100644
index 00000000..bc9ba6a0
--- /dev/null
+++ b/hamcrest/src/test/java/org/hamcrest/beans/PropertyUtilTest.java
@@ -0,0 +1,150 @@
+package org.hamcrest.beans;
+
+import org.junit.jupiter.api.Test;
+
+import java.beans.MethodDescriptor;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+
+class PropertyUtilTest {
+
+    @Test
+    void testReturnsTheNamesOfAllFieldsFromTargetClass() {
+        SamePropertyValuesAsTest.ExampleBean input = new SamePropertyValuesAsTest.ExampleBean("test", 1, null);
+
+        Set output = PropertyUtil.getFieldNames(input);
+
+        assertThat(output, hasSize(3));
+        assertThat(output, hasItems("stringProperty", "intProperty", "valueProperty"));
+        assertThat(output, not(hasItem("nonexistentField")));
+    }
+
+    @Test
+    void testReturnsTheNamesOfAllFieldsFromTargetRecord() {
+        RecordLikeClass.SmallClass smallClass1 = new RecordLikeClass.SmallClass();
+        RecordLikeClass.SmallClass smallClass2 = new RecordLikeClass.SmallClass("small", 3, BigDecimal.ONE, LocalDateTime.of(2024, 1, 2, 3, 4, 5));
+        RecordLikeClass input = new RecordLikeClass("uno", 22, true, new Long[] {1L, 2L, 3L}, new ArrayList<>(Arrays.asList(smallClass1, smallClass2)));
+
+        Set output = PropertyUtil.getFieldNames(input);
+
+        assertThat(output, hasSize(5));
+        assertThat(output, hasItems("numberArray", "test", "smallClasses", "name", "age"));
+        assertThat(output, not(hasItem("notAGetter1")));
+        assertThat(output, not(hasItem("notAGetter2")));
+        assertThat(output, not(hasItem("getAge")));
+        assertThat(output, not(hasItem("field1")));
+        assertThat(output, not(hasItem("nonexistentField")));
+    }
+
+    @Test
+    void testReturnsArrayOfMethodDescriptorFromTargetClass() {
+        SamePropertyValuesAsTest.ExampleBean input = new SamePropertyValuesAsTest.ExampleBean("test", 1, null);
+
+        MethodDescriptor[] output = PropertyUtil.recordReadAccessorMethodDescriptorsFor(input, Object.class);
+
+        assertThat(output, arrayWithSize(0));
+    }
+
+    @Test
+    void testReturnsArrayOfMethodDescriptorFromTargetRecord() {
+        RecordLikeClass.SmallClass smallClass1 = new RecordLikeClass.SmallClass();
+        RecordLikeClass.SmallClass smallClass2 = new RecordLikeClass.SmallClass("small", 3, BigDecimal.ONE, LocalDateTime.of(2024, 1, 2, 3, 4, 5));
+        RecordLikeClass input = new RecordLikeClass("uno", 22, true, new Long[] {1L, 2L, 3L}, new ArrayList<>(Arrays.asList(smallClass1, smallClass2)));
+
+        MethodDescriptor[] output = PropertyUtil.recordReadAccessorMethodDescriptorsFor(input, Object.class);
+
+        assertThat(output, arrayWithSize(5));
+        assertThat(Arrays.stream(output).map(MethodDescriptor::getDisplayName).collect(Collectors.toList()),
+                   hasItems("numberArray", "test", "smallClasses", "name", "age"));
+    }
+
+
+    /**
+     * A Java Record-like class to test the functionality of
+     * {@link PropertyUtil} with Java Records in JDK 8 environment.
+     *
+     * @see https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    @SuppressWarnings("unused")
+    static final class RecordLikeClass {
+        private final String name;
+        private final int age;
+        private final boolean test;
+        private final Long[] numberArray;
+        private final List smallClasses;
+
+        public RecordLikeClass(String name, int age, boolean test, Long[] numberArray, List smallClasses) {
+            this.name = name;
+            this.age = age;
+            this.test = test;
+            this.numberArray = numberArray;
+            this.smallClasses = smallClasses;
+        }
+
+        public String name() { return name; }
+        public int age() { return age; }
+        public boolean test() { return test; }
+        public Long[] numberArray() { return numberArray; }
+        public List smallClasses() { return smallClasses; }
+
+        public void notAGetter1() {}
+        public String notAGetter2() { return "I'm nothing"; }
+        public String name(String fake1, String fake2) { return name; }
+        public void name(String fake1) {}
+        public int getAge() { return 0; }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof RecordLikeClass)) return false;
+            RecordLikeClass that = (RecordLikeClass) o;
+            return this.age() == that.age() &&
+                    this.test() == that.test() &&
+                    Objects.equals(this.name(), that.name()) &&
+                    Objects.deepEquals(this.numberArray(), that.numberArray())&&
+                    Objects.equals(this.smallClasses(), that.smallClasses());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(name(), age(), test(), Arrays.hashCode(numberArray()), smallClasses());
+        }
+
+        @Override
+        public String toString() {
+            return "RecordLikeClass{" +
+                    "name='" + name + '\'' +
+                    ", age=" + age +
+                    ", test=" + test +
+                    ", numberArray=" + Arrays.toString(numberArray) +
+                    ", smallClasses=" + smallClasses +
+                    '}';
+        }
+
+        static class SmallClass {
+            private String field1;
+            private Integer field2;
+            private BigDecimal field3;
+            private LocalDateTime field4;
+
+            public SmallClass() {}
+
+            public SmallClass(String field1, Integer field2, BigDecimal field3, LocalDateTime field4) {
+                this.field1 = field1;
+                this.field2 = field2;
+                this.field3 = field3;
+                this.field4 = field4;
+            }
+
+            @Override
+            public String toString() {
+                return "SmallClass{field1='" + field1 + "', field2=" + field2 + ", field3=" + field3 + ", field4=" + field4 + '}';
+            }
+        }
+    }
+
+}
diff --git a/hamcrest/src/test/java/org/hamcrest/beans/SamePropertyValuesAsTest.java b/hamcrest/src/test/java/org/hamcrest/beans/SamePropertyValuesAsTest.java
index 778259b2..bd5f1796 100644
--- a/hamcrest/src/test/java/org/hamcrest/beans/SamePropertyValuesAsTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/beans/SamePropertyValuesAsTest.java
@@ -1,8 +1,12 @@
 package org.hamcrest.beans;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import java.util.Objects;
+
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
 
 @SuppressWarnings("WeakerAccess")
@@ -11,21 +15,30 @@ public class SamePropertyValuesAsTest extends AbstractMatcherTest {
   private static final Value aValue = new Value("expected");
   private static final ExampleBean expectedBean = new ExampleBean("same", 1, aValue);
   private static final ExampleBean actualBean = new ExampleBean("same", 1, aValue);
+  private static final ExampleRecord expectedRecord = new ExampleRecord("same", 1, aValue);
+  private static final ExampleRecord actualRecord = new ExampleRecord("same", 1, aValue);
+
 
   @Override
   protected Matcher createMatcher() {
     return samePropertyValuesAs(expectedBean);
   }
 
+  @Test
   public void test_reports_match_when_all_properties_match() {
     assertMatches("matched properties", samePropertyValuesAs(expectedBean), actualBean);
+    assertMatches("matched properties", samePropertyValuesAs(expectedRecord), actualRecord);
   }
 
+  @Test
   public void test_reports_mismatch_when_actual_type_is_not_assignable_to_expected_type() {
     assertMismatchDescription("is incompatible type: ExampleBean",
                               samePropertyValuesAs((Object)aValue), actualBean);
+    assertMismatchDescription("is incompatible type: ExampleRecord",
+                              samePropertyValuesAs((Object)aValue), actualRecord);
   }
 
+  @Test
   public void test_reports_mismatch_on_first_property_difference() {
     assertMismatchDescription("stringProperty was \"different\"",
         samePropertyValuesAs(expectedBean), new ExampleBean("different", 1, aValue));
@@ -33,48 +46,76 @@ public void test_reports_mismatch_on_first_property_difference() {
         samePropertyValuesAs(expectedBean), new ExampleBean("same", 2, aValue));
     assertMismatchDescription("valueProperty was ",
         samePropertyValuesAs(expectedBean), new ExampleBean("same", 1, new Value("other")));
+
+    assertMismatchDescription("stringProperty was \"different\"",
+        samePropertyValuesAs(expectedRecord), new ExampleRecord("different", 1, aValue));
+    assertMismatchDescription("intProperty was <2>",
+        samePropertyValuesAs(expectedRecord), new ExampleRecord("same", 2, aValue));
+    assertMismatchDescription("valueProperty was ",
+        samePropertyValuesAs(expectedRecord), new ExampleRecord("same", 1, new Value("other")));
   }
 
+  @Test
   public void test_matches_beans_with_inheritance_but_no_extra_properties() {
     assertMatches("sub type with same properties",
         samePropertyValuesAs(expectedBean), new SubBeanWithNoExtraProperties("same", 1, aValue));
   }
 
+  @Test
   public void test_rejects_subtype_that_has_extra_properties() {
     assertMismatchDescription("has extra properties called [extraProperty]",
         samePropertyValuesAs(expectedBean), new SubBeanWithExtraProperty("same", 1, aValue));
   }
 
+  @Test
   public void test_ignores_extra_subtype_properties() {
     final SubBeanWithExtraProperty withExtraProperty = new SubBeanWithExtraProperty("same", 1, aValue);
     assertMatches("extra property", samePropertyValuesAs(expectedBean, "extraProperty"), withExtraProperty);
   }
 
+  @Test
   public void test_ignores_different_properties() {
     final ExampleBean differentBean = new ExampleBean("different", 1, aValue);
+    final ExampleRecord differentRecord = new ExampleRecord("different", 1, aValue);
     assertMatches("different property", samePropertyValuesAs(expectedBean, "stringProperty"), differentBean);
+    assertMatches("different property", samePropertyValuesAs(expectedRecord, "stringProperty"), differentRecord);
   }
 
+  @Test
   public void test_accepts_missing_properties_to_ignore() {
     assertMatches("ignored property", samePropertyValuesAs(expectedBean, "notAProperty"), actualBean);
+    assertMatches("ignored property", samePropertyValuesAs(expectedRecord, "notAProperty"), actualRecord);
   }
 
+  @Test
   public void test_can_ignore_all_properties() {
     final ExampleBean differentBean = new ExampleBean("different", 2, new Value("not expected"));
+    final ExampleRecord differentRecord = new ExampleRecord("different", 2, new Value("not expected"));
     assertMatches(
             "different property",
             samePropertyValuesAs(expectedBean, "stringProperty", "intProperty", "valueProperty"),
             differentBean);
+    assertMatches(
+            "different property",
+            samePropertyValuesAs(expectedRecord, "stringProperty", "intProperty", "valueProperty"),
+            differentRecord);
   }
 
+  @Test
   public void testDescribesItself() {
     assertDescription(
             "same property values as ExampleBean [intProperty: <1>, stringProperty: \"same\", valueProperty: ]",
             samePropertyValuesAs(expectedBean));
+    assertDescription(
+            "same property values as ExampleRecord [valueProperty: , stringProperty: \"same\", intProperty: <1>]",
+            samePropertyValuesAs(expectedRecord));
 
     assertDescription(
             "same property values as ExampleBean [intProperty: <1>, stringProperty: \"same\", valueProperty: ] ignoring [\"ignored1\", \"ignored2\"]",
             samePropertyValuesAs(expectedBean, "ignored1", "ignored2"));
+    assertDescription(
+            "same property values as ExampleRecord [valueProperty: , stringProperty: \"same\", intProperty: <1>] ignoring [\"ignored1\", \"ignored2\"]",
+            samePropertyValuesAs(expectedRecord, "ignored1", "ignored2"));
   }
 
   public static class Value {
@@ -114,6 +155,45 @@ public Value getValueProperty() {
     @Override public String toString() { return "an ExampleBean"; }
   }
 
+  /**
+   * A Java Record-like class to test the functionality of
+   * {@link SamePropertyValuesAs} with Java Records in JDK 8 environment.
+   * The basic property structure is the same as {@link ExampleBean ExampleBean} for the exact comparison.
+   *
+   * @see ExampleBean ExampleBean
+   * @see https://docs.oracle.com/en/java/javase/17/language/records.html
+   */
+  @SuppressWarnings("unused")
+  public static final class ExampleRecord {
+    private final String stringProperty;
+    private final int intProperty;
+    private final Value valueProperty;
+
+    public ExampleRecord(String stringProperty, int intProperty, Value valueProperty) {
+      this.stringProperty = stringProperty;
+      this.intProperty = intProperty;
+      this.valueProperty = valueProperty;
+    }
+
+    public String stringProperty() { return stringProperty; }
+    public int intProperty() { return intProperty; }
+    public Value valueProperty() { return valueProperty; }
+
+    @Override
+    public boolean equals(Object o) {
+      if (!(o instanceof ExampleRecord)) return false;
+      ExampleRecord that = (ExampleRecord) o;
+      return this.intProperty == that.intProperty && Objects.equals(this.stringProperty, that.stringProperty) && Objects.equals(this.valueProperty, that.valueProperty);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(stringProperty, intProperty, valueProperty);
+    }
+
+    @Override public String toString() { return "an ExampleRecord"; }
+  }
+
   public static class SubBeanWithNoExtraProperties extends ExampleBean {
     public SubBeanWithNoExtraProperties(String stringProperty, int intProperty, Value valueProperty) {
       super(stringProperty, intProperty, valueProperty);
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/ArrayMatchingInAnyOrderTest.java b/hamcrest/src/test/java/org/hamcrest/collection/ArrayMatchingInAnyOrderTest.java
index e2cb734c..6c5f19f7 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/ArrayMatchingInAnyOrderTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/ArrayMatchingInAnyOrderTest.java
@@ -1,8 +1,10 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.IsEqual.equalTo;
 
 public class ArrayMatchingInAnyOrderTest extends AbstractMatcherTest {
@@ -13,18 +15,21 @@ protected Matcher createMatcher() {
         return ArrayMatching.arrayContainingInAnyOrder(equalTo(1), equalTo(2));
     }
 
+    @Test
     @SuppressWarnings("unchecked")
     public void testHasAReadableDescription() {
         assertDescription("[<1>, <2>] in any order", ArrayMatching.arrayContainingInAnyOrder(equalTo(1), equalTo(2)));
         assertDescription("[<1>, <2>] in any order", ArrayMatching.arrayContainingInAnyOrder(1, 2));
     }
 
+    @Test
     public void testMatchesItemsInAnyOrder() {
       assertMatches("in order", ArrayMatching.arrayContainingInAnyOrder(1, 2, 3), new Integer[] {1, 2, 3});
       assertMatches("out of order", ArrayMatching.arrayContainingInAnyOrder(1, 2, 3), new Integer[] {3, 2, 1});
       assertMatches("single", ArrayMatching.arrayContainingInAnyOrder(1), new Integer[] {1});
     }
 
+    @Test
     @SuppressWarnings("unchecked")
     public void testAppliesMatchersInAnyOrder() {
       assertMatches("in order", ArrayMatching.arrayContainingInAnyOrder(equalTo(1), equalTo(2), equalTo(3)), new Integer[] {1, 2, 3});
@@ -32,6 +37,7 @@ public void testAppliesMatchersInAnyOrder() {
       assertMatches("single", ArrayMatching.arrayContainingInAnyOrder(equalTo(1)), new Integer[] {1});
     }
 
+    @Test
     public void testMismatchesItemsInAnyOrder() {
       Matcher matcher = ArrayMatching.arrayContainingInAnyOrder(1, 2, 3);
       assertMismatchDescription("was null", matcher, null);
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/ArrayMatchingInOrderTest.java b/hamcrest/src/test/java/org/hamcrest/collection/ArrayMatchingInOrderTest.java
index 99db00a8..e19edfca 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/ArrayMatchingInOrderTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/ArrayMatchingInOrderTest.java
@@ -1,8 +1,10 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.ArrayMatching.arrayContaining;
 import static org.hamcrest.core.IsEqual.equalTo;
 
@@ -14,22 +16,26 @@ protected Matcher createMatcher() {
         return arrayContaining(equalTo(1), equalTo(2));
     }
 
+    @Test
     @SuppressWarnings("unchecked")
     public void testHasAReadableDescription() {
         assertDescription("[<1>, <2>]", arrayContaining(equalTo(1), equalTo(2)));
     }
 
+    @Test
     public void testMatchesItemsInOrder() {
       assertMatches("in order", arrayContaining(1, 2, 3), new Integer[] {1, 2, 3});
       assertMatches("single", arrayContaining(1), new Integer[] {1});
     }
 
+    @Test
     @SuppressWarnings("unchecked")
     public void testAppliesMatchersInOrder() {
       assertMatches("in order", arrayContaining(equalTo(1), equalTo(2), equalTo(3)), new Integer[] {1, 2, 3});
       assertMatches("single", arrayContaining(equalTo(1)), new Integer[] {1});
     }
 
+    @Test
     public void testMismatchesItemsInOrder() {
       Matcher matcher = arrayContaining(1, 2, 3);
       assertMismatchDescription("was null", matcher, null);
@@ -39,6 +45,7 @@ public void testMismatchesItemsInOrder() {
       assertMismatchDescription("item 2: was <4>", matcher, new Integer[] {1,2, 4});
     }
 
+    @Test
     public void testCanHandleNullValuesInAnArray() {
       assertMatches("with nulls", arrayContaining(null, null), new Object[]{null, null});
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/HasItemInArrayTest.java b/hamcrest/src/test/java/org/hamcrest/collection/HasItemInArrayTest.java
index c4d7e34a..cb810217 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/HasItemInArrayTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/HasItemInArrayTest.java
@@ -1,8 +1,10 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.Matchers.lessThan;
 import static org.hamcrest.collection.ArrayMatching.hasItemInArray;
 
@@ -13,11 +15,13 @@ protected Matcher createMatcher() {
         return hasItemInArray("irrelevant");
     }
 
+    @Test
     public void testMatchesAnArrayThatContainsAnElementMatchingTheGivenMatcher() {
         assertMatches("should matches array that contains 'a'",
                 hasItemInArray("a"), new String[]{"a", "b", "c"});
     }
 
+    @Test
     public void testDoesNotMatchAnArrayThatDoesntContainAnElementMatchingTheGivenMatcher() {
         assertDoesNotMatch("should not matches array that doesn't contain 'a'",
                 hasItemInArray("a"), new String[]{"b", "c"});
@@ -28,49 +32,14 @@ public void testDoesNotMatchAnArrayThatDoesntContainAnElementMatchingTheGivenMat
               hasItemInArray(lessThan(2)), new Integer[] {3, 4, 5});
     }
 
+    @Test
     public void testDoesNotMatchNull() {
         assertDoesNotMatch("should not matches null",
                 hasItemInArray("a"), null);
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("an array containing a value less than <2>", hasItemInArray(lessThan(2)));
     }
-
-    // Remaining code no longer compiles, thanks to generics. I think that's a good thing, but
-    // I still need to investigate how this behaves with code that doesn't use generics.
-    // I expect ClassCastExceptions will be thrown.
-    // -Joe.
-
-//    public void testDoesNotMatchObjectThatIsNotAnArray() {
-//        assertDoesNotMatch("should not matches empty list",
-//                arrayContaining("a"), "not a collection");
-//    }
-
-//    public void testMatchesPrimitiveArrayElements() {
-//        assertMatches("boolean", arrayContaining(true), new boolean[]{true, false});
-//        assertDoesNotMatch("boolean", arrayContaining(false), new boolean[]{false});
-//
-//        assertMatches("byte", arrayContaining((byte) 1), new byte[]{1, 2, 3});
-//        assertDoesNotMatch("byte", arrayContaining((byte) 0), new byte[]{1, 2, 3});
-//
-//        assertMatches("char", arrayContaining('a'), new char[]{'a', 'b', 'c'});
-//        assertDoesNotMatch("char", arrayContaining('z'), new char[]{'a', 'b', 'c'});
-//
-//        assertMatches("short", arrayContaining((short) 1), new short[]{1, 2, 3});
-//        assertDoesNotMatch("short", arrayContaining((short) 0), new short[]{1, 2, 3});
-//
-//        assertMatches("int", arrayContaining(1), new int[]{1, 2, 3});
-//        assertDoesNotMatch("int", arrayContaining(0), new int[]{1, 2, 3});
-//
-//        assertMatches("long", arrayContaining(1L), new long[]{1, 2, 3});
-//        assertDoesNotMatch("long", arrayContaining(0L), new long[]{1, 2, 3});
-//
-//        assertMatches("float", arrayContaining(1f), new float[]{1f, 2f, 3f});
-//        assertDoesNotMatch("float", arrayContaining(0f), new float[]{1f, 2f, 3f});
-//
-//        assertMatches("double", arrayContaining(1.0), new double[]{1.0, 2.0, 3.0});
-//        assertDoesNotMatch("double", arrayContaining(0.0), new double[]{1.0, 2.0, 3.0});
-//    }
-
 }
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsArrayContainingInAnyOrderTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsArrayContainingInAnyOrderTest.java
index ce5d9931..5fb93ca0 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsArrayContainingInAnyOrderTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsArrayContainingInAnyOrderTest.java
@@ -1,10 +1,12 @@
 package org.hamcrest.collection;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsArrayContainingInAnyOrder.arrayContainingInAnyOrder;
 import static org.hamcrest.core.IsEqual.equalTo;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 public class IsArrayContainingInAnyOrderTest extends AbstractMatcherTest {
 
@@ -14,18 +16,21 @@ protected Matcher createMatcher() {
         return arrayContainingInAnyOrder(equalTo(1), equalTo(2));
     }
 
+    @Test
     @SuppressWarnings("unchecked")
     public void testHasAReadableDescription() {
         assertDescription("[<1>, <2>] in any order", arrayContainingInAnyOrder(equalTo(1), equalTo(2)));
         assertDescription("[<1>, <2>] in any order", arrayContainingInAnyOrder(1, 2));
     }
 
+    @Test
     public void testMatchesItemsInAnyOrder() {
       assertMatches("in order", arrayContainingInAnyOrder(1, 2, 3), new Integer[] {1, 2, 3});
       assertMatches("out of order", arrayContainingInAnyOrder(1, 2, 3), new Integer[] {3, 2, 1});
       assertMatches("single", arrayContainingInAnyOrder(1), new Integer[] {1});
     }
 
+    @Test
     @SuppressWarnings("unchecked")
     public void testAppliesMatchersInAnyOrder() {
       assertMatches("in order", arrayContainingInAnyOrder(equalTo(1), equalTo(2), equalTo(3)), new Integer[] {1, 2, 3});
@@ -33,6 +38,7 @@ public void testAppliesMatchersInAnyOrder() {
       assertMatches("single", arrayContainingInAnyOrder(equalTo(1)), new Integer[] {1});
     }
 
+    @Test
     public void testMismatchesItemsInAnyOrder() {
       Matcher matcher = arrayContainingInAnyOrder(1, 2, 3);
       assertMismatchDescription("was null", matcher, null);
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsArrayContainingInOrderTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsArrayContainingInOrderTest.java
index 6713e687..e827a317 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsArrayContainingInOrderTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsArrayContainingInOrderTest.java
@@ -1,10 +1,12 @@
 package org.hamcrest.collection;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsArrayContainingInOrder.arrayContaining;
 import static org.hamcrest.core.IsEqual.equalTo;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 public class IsArrayContainingInOrderTest extends AbstractMatcherTest {
 
@@ -14,22 +16,26 @@ protected Matcher createMatcher() {
         return arrayContaining(equalTo(1), equalTo(2));
     }
 
+    @Test
     @SuppressWarnings("unchecked")
     public void testHasAReadableDescription() {
         assertDescription("[<1>, <2>]", arrayContaining(equalTo(1), equalTo(2)));
     }
 
+    @Test
     public void testMatchesItemsInOrder() {
       assertMatches("in order", arrayContaining(1, 2, 3), new Integer[] {1, 2, 3});
       assertMatches("single", arrayContaining(1), new Integer[] {1});
     }
 
+    @Test
     @SuppressWarnings("unchecked")
     public void testAppliesMatchersInOrder() {
       assertMatches("in order", arrayContaining(equalTo(1), equalTo(2), equalTo(3)), new Integer[] {1, 2, 3});
       assertMatches("single", arrayContaining(equalTo(1)), new Integer[] {1});
     }
 
+    @Test
     public void testMismatchesItemsInOrder() {
       Matcher matcher = arrayContaining(1, 2, 3);
       assertMismatchDescription("was null", matcher, null);
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsArrayTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsArrayTest.java
index 70a3ff2c..7aeb06a4 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsArrayTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsArrayTest.java
@@ -1,10 +1,12 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsArray.array;
 import static org.hamcrest.core.IsEqual.equalTo;
 
@@ -16,16 +18,19 @@ protected Matcher createMatcher() {
         return array(equalTo("irrelevant"));
     }
 
+    @Test
     public void testMatchesAnArrayThatMatchesAllTheElementMatchers() {
         assertMatches("should match array with matching elements",
                 array(equalTo("a"), equalTo("b"), equalTo("c")), new String[]{"a", "b", "c"});
     }
 
+    @Test
     public void testDoesNotMatchAnArrayWhenElementsDoNotMatch() {
         assertDoesNotMatch("should not match array with different elements",
                 array(equalTo("a"), equalTo("b")), new String[]{"b", "c"});
     }
 
+    @Test
     public void testDoesNotMatchAnArrayOfDifferentSize() {
         assertDoesNotMatch("should not match larger array",
                            array(equalTo("a"), equalTo("b")), new String[]{"a", "b", "c"});
@@ -33,19 +38,23 @@ public void testDoesNotMatchAnArrayOfDifferentSize() {
                            array(equalTo("a"), equalTo("b")), new String[]{"a"});
     }
 
+    @Test
     public void testDoesNotMatchNull() {
         assertDoesNotMatch("should not match null",
                 array(equalTo("a")), null);
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("[\"a\", \"b\"]", array(equalTo("a"), equalTo("b")));
     }
 
+    @Test
     public void testHasAReadableMismatchDescriptionUsing() {
         assertMismatchDescription("element <0> was \"c\"", array(equalTo("a"), equalTo("b")), new String[]{"c", "b"});
     }
 
+    @Test
     public void testHasAReadableMismatchDescriptionUsingCustomMatchers() {
         final BaseMatcher m = new BaseMatcher() {
             @Override public boolean matches(Object item) { return false; }
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsArrayWithSizeTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsArrayWithSizeTest.java
index 051021a9..700825dd 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsArrayWithSizeTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsArrayWithSizeTest.java
@@ -1,8 +1,10 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsArrayWithSize.arrayWithSize;
 import static org.hamcrest.collection.IsArrayWithSize.emptyArray;
 import static org.hamcrest.core.IsEqual.equalTo;
@@ -14,21 +16,25 @@ protected Matcher createMatcher() {
         return arrayWithSize(equalTo(2));
     }
 
+    @Test
     public void testMatchesWhenSizeIsCorrect() {
         assertMatches("correct size", arrayWithSize(equalTo(3)), new Object[] {1, 2, 3});
         assertDoesNotMatch("incorrect size", arrayWithSize(equalTo(2)), new Object[] {1, 2, 3});
     }
 
+    @Test
     public void testProvidesConvenientShortcutForArrayWithSizeEqualTo() {
         assertMatches("correct size", arrayWithSize(3), new Object[] {1, 2, 3});
         assertDoesNotMatch("incorrect size", arrayWithSize(2), new Object[] {1, 2, 3});
     }
 
+    @Test
     public void testEmptyArray() {
         assertMatches("correct size", emptyArray(), new Object[] {});
         assertDoesNotMatch("incorrect size", emptyArray(), new Object[] {1});
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("an array with size <3>", arrayWithSize(equalTo(3)));
         assertDescription("an empty array", emptyArray());
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsCollectionWithSizeTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsCollectionWithSizeTest.java
index 0d6a3eed..63f91b98 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsCollectionWithSizeTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsCollectionWithSizeTest.java
@@ -1,14 +1,16 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
 import org.hamcrest.MatcherAssert;
+import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
 import static java.util.Arrays.asList;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
 import static org.hamcrest.core.IsEqual.equalTo;
 
@@ -19,56 +21,66 @@ protected Matcher createMatcher() {
         return hasSize(7);
     }
 
+    @Test
     public void testMatchesWhenSizeIsCorrect() {
         assertMatches("correct size", hasSize(equalTo(2)), asList(null, null));
         assertMismatchDescription("collection size was <3>", hasSize(equalTo(2)), asList(null, null, null));
     }
 
+    @Test
     public void testMatchesCollectionWhenSizeIsCorrectUsingObjectElementType() {
         Collection list = asList(null, null);
         assertMatches("correct size", hasSize(equalTo(2)), list);
         assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
     }
 
+    @Test
     public void testMatchesCollectionWhenSizeIsCorrectUsingStringElementType() {
         Collection list = asList("a", "b");
         assertMatches("correct size", hasSize(equalTo(2)), list);
         assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
     }
 
+    @Test
     public void testMatchesCollectionWhenSizeIsCorrectUsingWildcardElementType() {
         Collection list = asList("a", "b");
         assertMatches("correct size", hasSize(equalTo(2)), list);
         assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
     }
 
+    @Test
     public void testMatchesListWhenSizeIsCorrectUsingObjectElementType() {
         List list = asList(null, null);
         assertMatches("correct size", hasSize(equalTo(2)), list);
         assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
     }
 
+    @Test
     public void testMatchesListWhenSizeIsCorrectUsingStringElementType() {
         List list = asList("a", "b");
         assertMatches("correct size", hasSize(equalTo(2)), list);
         assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
     }
 
+    @Test
     public void testMatchesListWhenSizeIsCorrectUsingWildcardElementType() {
         List list = asList("a", "b");
         assertMatches("correct size", hasSize(equalTo(2)), list);
         assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
     }
 
+    @Test
     public void testProvidesConvenientShortcutForHasSizeEqualTo() {
         assertMatches("correct size", hasSize(2), asList(null, null));
         assertMismatchDescription("collection size was <3>", hasSize(2), asList(null, null, null));
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("a collection with size <3>", hasSize(equalTo(3)));
     }
 
+    @Test
     public void testCompilesWithATypedCollection() {
       // To prove Issue 43
       ArrayList arrayList = new ArrayList<>();
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsEmptyCollectionTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsEmptyCollectionTest.java
index 4422a2ab..94783490 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsEmptyCollectionTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsEmptyCollectionTest.java
@@ -1,12 +1,14 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
 import java.util.Collection;
 
 import static java.util.Arrays.asList;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsEmptyCollection.empty;
 import static org.hamcrest.core.Is.is;
 
@@ -17,18 +19,22 @@ protected Matcher> createMatcher() {
         return empty();
     }
 
+    @Test
     public void testMatchesAnEmptyCollection() {
         assertMatches("empty collection", createMatcher(), emptyCollection());
     }
 
+    @Test
     public void testDoesNotMatchACollectionWithAnItem() {
         assertMismatchDescription("<[one, three]>", is(createMatcher()), collectionOfValues());
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("an empty collection", createMatcher());
     }
 
+    @Test
     public void testCompiles() {
         needs(IsEmptyCollection.emptyCollectionOf(String.class));
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsEmptyIterableTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsEmptyIterableTest.java
index 37027c50..eb83e898 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsEmptyIterableTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsEmptyIterableTest.java
@@ -1,12 +1,14 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
 import java.util.Collection;
 
 import static java.util.Arrays.asList;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsEmptyIterable.emptyIterable;
 
 public class IsEmptyIterableTest extends AbstractMatcherTest {
@@ -16,18 +18,22 @@ protected Matcher> createMatcher() {
         return emptyIterable();
     }
 
+    @Test
     public void testMatchesAnEmptyIterable() {
         assertMatches("empty iterable", createMatcher(), emptyCollection());
     }
 
+    @Test
     public void testDoesNotMatchAnIterableWithItems() {
         assertDoesNotMatch("iterable with an item", createMatcher(), collectionOfValues());
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("an empty iterable", createMatcher());
     }
 
+    @Test
     public void testCompiles() {
         needs(IsEmptyIterable.emptyIterableOf(String.class));
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsInTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsInTest.java
index 87fccc0a..5e6fd4d3 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsInTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsInTest.java
@@ -1,12 +1,17 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
 import org.hamcrest.StringDescription;
+import org.junit.jupiter.api.Test;
 
 import java.util.Arrays;
 import java.util.Collection;
 
+import static org.hamcrest.test.MatcherAssertions.assertDoesNotMatch;
+import static org.hamcrest.test.MatcherAssertions.assertMatches;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
 public class IsInTest extends AbstractMatcherTest {
 
     String[] elements = {"a", "b", "c"};
@@ -16,6 +21,7 @@ protected Matcher createMatcher() {
         return new IsIn<>(elements);
     }
 
+    @Test
     public void testReturnsTrueIfArgumentIsInCollection() {
         Collection collection = Arrays.asList(elements);
         Matcher isIn = new IsIn<>(collection);
@@ -26,6 +32,7 @@ public void testReturnsTrueIfArgumentIsInCollection() {
         assertDoesNotMatch("d", isIn, "d");
     }
 
+    @Test
     public void testReturnsTrueIfArgumentIsInArray() {
         Matcher isIn = new IsIn<>(elements);
 
@@ -35,12 +42,14 @@ public void testReturnsTrueIfArgumentIsInArray() {
         assertDoesNotMatch("d", isIn, "d");
     }
 
+    @Test
     public void testHasReadableDescription() {
         Matcher isIn = new IsIn<>(elements);
 
-        assertEquals("description",
-            "one of {\"a\", \"b\", \"c\"}",
-            StringDescription.toString(isIn));
+        assertEquals(
+                "one of {\"a\", \"b\", \"c\"}",
+                StringDescription.toString(isIn),
+                "description");
     }
 
 }
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInAnyOrderTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInAnyOrderTest.java
index d27ff3ff..057b3b4d 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInAnyOrderTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInAnyOrderTest.java
@@ -1,12 +1,14 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
 import org.hamcrest.collection.IsIterableContainingInOrderTest.WithValue;
+import org.junit.jupiter.api.Test;
 
 import java.util.Collections;
 
 import static java.util.Arrays.asList;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
 import static org.hamcrest.collection.IsIterableContainingInOrderTest.make;
 import static org.hamcrest.collection.IsIterableContainingInOrderTest.value;
@@ -18,36 +20,44 @@ protected Matcher createMatcher() {
         return containsInAnyOrder(1, 2);
     }
 
+    @Test
     public void testMatchesSingleItemIterable() {
       assertMatches("single item", containsInAnyOrder(1), asList(1));
     }
 
+    @Test
     public void testDoesNotMatchEmpty() {
         assertMismatchDescription("no item matches: <1>, <2> in []", containsInAnyOrder(1, 2), Collections.emptyList());
     }
 
+    @Test
     public void testMatchesIterableOutOfOrder() {
         assertMatches("Out of order", containsInAnyOrder(1, 2), asList(2, 1));
     }
 
+    @Test
     public void testMatchesIterableInOrder() {
         assertMatches("In order", containsInAnyOrder(1, 2), asList(1, 2));
     }
 
+    @Test
     public void testDoesNotMatchIfOneOfMultipleElementsMismatches() {
         assertMismatchDescription("not matched: <4>", containsInAnyOrder(1, 2, 3), asList(1, 2, 4));
     }
 
+    @Test
     @SuppressWarnings("unchecked")
     public void testDoesNotMatchIfThereAreMoreElementsThanMatchers() {
         final Matcher> helpTheCompilerOut = containsInAnyOrder(value(1), value(3));
         assertMismatchDescription("not matched: ", helpTheCompilerOut, asList(make(1), make(2), make(3)));
     }
 
+    @Test
     public void testDoesNotMatchIfThereAreMoreMatchersThanElements() {
         assertMismatchDescription("no item matches: <4> in [<1>, <2>, <3>]", containsInAnyOrder(1, 2, 3, 4), asList(1, 2, 3));
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("iterable with items [<1>, <2>] in any order", containsInAnyOrder(1, 2));
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInOrderTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInOrderTest.java
index be7f1728..bc318bb1 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInOrderTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInOrderTest.java
@@ -1,13 +1,15 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.FeatureMatcher;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
 import static org.hamcrest.core.IsEqual.equalTo;
 
@@ -22,38 +24,47 @@ protected Matcher createMatcher() {
         return contains(1, 2);
     }
 
+    @Test
     public void testMatchingSingleItemIterable() throws Exception {
         assertMatches("Single item iterable", contains(1), singletonList(1));
     }
 
+    @Test
     public void testMatchingMultipleItemIterable() throws Exception {
         assertMatches("Multiple item iterable", contains(1, 2, 3), asList(1, 2, 3));
     }
 
+    @Test
     public void testDoesNotMatchWithMoreElementsThanExpected() throws Exception {
         assertMismatchDescription("not matched: <4>", contains(1, 2, 3), asList(1, 2, 3, 4));
     }
 
+    @Test
     public void testDoesNotMatchWithFewerElementsThanExpected() throws Exception {
         assertMismatchDescription("no item was value with <3>", contains123, asList(make(1), make(2)));
     }
 
+    @Test
     public void testDoesNotMatchIfSingleItemMismatches() throws Exception {
         assertMismatchDescription("item 0: value was <3>", contains(value(4)), singletonList(make(3)));
     }
 
+    @Test
     public void testDoesNotMatchIfOneOfMultipleItemsMismatch() throws Exception {
         assertMismatchDescription("item 2: value was <4>", contains123, asList(make(1), make(2), make(4)));
     }
 
+    @Test
     public void testDoesNotMatchEmptyIterable() throws Exception {
         assertMismatchDescription("no item was value with <4>", contains(value(4)), new ArrayList());
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("iterable containing [<1>, <2>]", contains(1, 2));
     }
 
+    @Test
     public void testCanHandleNullMatchers() {
         assertMatches(contains(null, null), asList(null, null));
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInRelativeOrderTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInRelativeOrderTest.java
index 1d4d83ee..dc8662ad 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInRelativeOrderTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableContainingInRelativeOrderTest.java
@@ -1,13 +1,15 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.FeatureMatcher;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
 import java.util.List;
 
 import static java.util.Arrays.asList;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsIterableContainingInRelativeOrder.containsInRelativeOrder;
 import static org.hamcrest.core.IsEqual.equalTo;
 
@@ -22,55 +24,68 @@ protected Matcher createMatcher() {
         return containsInRelativeOrder(1, 2);
     }
 
+    @Test
     public void testMatchingSingleItemIterable() throws Exception {
         assertMatches("Single item iterable", containsInRelativeOrder(1), asList(1));
     }
 
+    @Test
     public void testMatchingMultipleItemIterable() throws Exception {
         assertMatches("Multiple item iterable", containsInRelativeOrder(1, 2, 3), asList(1, 2, 3));
     }
 
+    @Test
     public void testMatchesWithMoreElementsThanExpectedAtBeginning() throws Exception {
         assertMatches("More elements at beginning", containsInRelativeOrder(2, 3, 4), asList(1, 2, 3, 4));
     }
 
+    @Test
     public void testMatchesWithMoreElementsThanExpectedAtEnd() throws Exception {
         assertMatches("More elements at end", containsInRelativeOrder(1, 2, 3), asList(1, 2, 3, 4));
     }
 
+    @Test
     public void testMatchesWithMoreElementsThanExpectedInBetween() throws Exception {
         assertMatches("More elements in between", containsInRelativeOrder(1, 3), asList(1, 2, 3));
     }
 
+    @Test
     public void testMatchesSubSection() throws Exception {
         assertMatches("Sub section of iterable", containsInRelativeOrder(2, 3), asList(1, 2, 3, 4));
     }
 
+    @Test
     public void testMatchesWithSingleGapAndNotFirstOrLast() throws Exception {
         assertMatches("Sub section with single gaps without a first or last match", containsInRelativeOrder(2, 4), asList(1, 2, 3, 4, 5));
     }
 
+    @Test
     public void testMatchingSubSectionWithManyGaps() throws Exception {
         assertMatches("Sub section with many gaps iterable", containsInRelativeOrder(2, 4, 6), asList(1, 2, 3, 4, 5, 6, 7));
     }
 
+    @Test
     public void testDoesNotMatchWithFewerElementsThanExpected() throws Exception {
         List valueList = asList(make(1), make(2));
         assertMismatchDescription("value with <3> was not found after ", contains123, valueList);
     }
 
+    @Test
     public void testDoesNotMatchIfSingleItemNotFound() throws Exception {
         assertMismatchDescription("value with <4> was not found", containsInRelativeOrder(value(4)), asList(make(3)));
     }
 
+    @Test
     public void testDoesNotMatchIfOneOfMultipleItemsNotFound() throws Exception {
         assertMismatchDescription("value with <3> was not found after ", contains123, asList(make(1), make(2), make(4)));
     }
 
+    @Test
     public void testDoesNotMatchEmptyIterable() throws Exception {
         assertMismatchDescription("value with <4> was not found", containsInRelativeOrder(value(4)), new ArrayList());
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("iterable containing [<1>, <2>] in relative order", containsInRelativeOrder(1, 2));
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableWithSizeTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableWithSizeTest.java
index c4ffd365..7cad4271 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableWithSizeTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableWithSizeTest.java
@@ -1,11 +1,13 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.util.Arrays;
 import java.util.Collections;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsIterableWithSize.iterableWithSize;
 
 public class IsIterableWithSizeTest extends AbstractMatcherTest {
@@ -15,22 +17,27 @@ protected Matcher createMatcher() {
         return iterableWithSize(7);
     }
 
+    @Test
     public void testMatchesEmptyIterable() throws Exception {
         assertMatches("Empty iterable", iterableWithSize(0), Collections.emptyList());
     }
 
+    @Test
     public void testMatchingSingleItemIterable() throws Exception {
         assertMatches("Single item iterable", iterableWithSize(1), Arrays.asList(1));
     }
 
+    @Test
     public void testMatchingMultipleItemIterable() throws Exception {
         assertMatches("Multiple item iterable", iterableWithSize(3), Arrays.asList(1, 2, 3));
     }
 
+    @Test
     public void testDoesNotMatchIncorrectSize() throws Exception {
         assertDoesNotMatch("Incorrect size", iterableWithSize(3), Arrays.asList(1));
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("an iterable with size <4>", iterableWithSize(4));
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingKeyTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingKeyTest.java
index db25b68e..bfe2aa82 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingKeyTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingKeyTest.java
@@ -1,13 +1,15 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsMapContaining.hasKey;
 
 public class IsMapContainingKeyTest extends AbstractMatcherTest {
@@ -17,6 +19,7 @@ protected Matcher createMatcher() {
         return hasKey("foo");
     }
 
+    @Test
     public void testMatchesSingletonMapContainingKey() {
         Map map = new HashMap<>();
         map.put("a", 1);
@@ -24,6 +27,7 @@ public void testMatchesSingletonMapContainingKey() {
         assertMatches("Matches single key", hasKey("a"), map);
     }
 
+    @Test
     public void testMatchesMapContainingKey() {
         Map map = new HashMap<>();
         map.put("a", 1);
@@ -45,6 +49,7 @@ public void testMatchesMapContainingKey() {
 //        assertMatches("Matches c", hasKey("c"), map);
 //    }
 
+    @Test
     public void testMatchesMapContainingKeyWithIntegerKeys() throws Exception {
         Map map = new HashMap<>();
         map.put(1, "A");
@@ -53,6 +58,7 @@ public void testMatchesMapContainingKeyWithIntegerKeys() throws Exception {
         assertThat(map, hasKey(1));
     }
 
+    @Test
     public void testMatchesMapContainingKeyWithNumberKeys() throws Exception {
         Map map = new HashMap<>();
         map.put(1, "A");
@@ -64,14 +70,17 @@ public void testMatchesMapContainingKeyWithNumberKeys() throws Exception {
 //        assertThat(map, hasKey(1));
     }
 
+    @Test
     public void testHasReadableDescription() {
         assertDescription("map containing [\"a\"->ANYTHING]", hasKey("a"));
     }
 
+    @Test
     public void testDoesNotMatchEmptyMap() {
         assertMismatchDescription("map was []", hasKey("Foo"), new HashMap());
     }
 
+    @Test
     public void testDoesNotMatchMapMissingKey() {
         Map map = new TreeMap<>();
         map.put("a", 1);
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingTest.java
index 762b5388..5c16f9b0 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingTest.java
@@ -1,16 +1,19 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsMapContaining.hasEntry;
 import static org.hamcrest.core.IsAnything.anything;
 import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 
 public class IsMapContainingTest extends AbstractMatcherTest {
 
@@ -19,6 +22,7 @@ protected Matcher createMatcher() {
         return IsMapContaining.hasEntry("irrelevant", "irrelevant");
     }
 
+    @Test
     public void testMatchesMapContainingMatchingKeyAndValue() {
         Map map = new TreeMap<>();
         map.put("a", 1);
@@ -29,6 +33,7 @@ public void testMatchesMapContainingMatchingKeyAndValue() {
         assertMismatchDescription("map was [, ]", hasEntry(equalTo("c"), equalTo(3)), map);
     }
 
+    @Test
     @SuppressWarnings("unchecked")
     public void testMatchesMapContainingMatchingKeyAndValueWithoutGenerics() {
         Map map = new HashMap();
@@ -37,17 +42,20 @@ public void testMatchesMapContainingMatchingKeyAndValueWithoutGenerics() {
 
         assertMatches("matcherA", hasEntry(equalTo("a"), equalTo(1)), map);
         assertMatches("matcherB", hasEntry(equalTo("b"), equalTo(2)), map);
-        assertFalse("matcherC", hasEntry(equalTo("c"), equalTo(3)).matches(map)); // working around generics problem
+        assertFalse(hasEntry(equalTo("c"), equalTo(3)).matches(map), "matcherC"); // working around generics problem
     }
 
+    @Test
     public void testDoesNotMatchNull() {
         assertMismatchDescription("was null", hasEntry(anything(), anything()), null);
     }
 
+    @Test
     public void testHasReadableDescription() {
         assertDescription("map containing [\"a\"-><2>]", hasEntry(equalTo("a"), (equalTo(2))));
     }
 
+    @Test
     public void testTypeVariance() {
         Map m = new HashMap<>();
         Integer foo = 6;
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingValueTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingValueTest.java
index ae657742..39b1abaf 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingValueTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsMapContainingValueTest.java
@@ -1,12 +1,14 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsMapContaining.hasValue;
 
 public class IsMapContainingValueTest extends AbstractMatcherTest {
@@ -16,15 +18,18 @@ protected Matcher createMatcher() {
         return hasValue("foo");
     }
 
+    @Test
     public void testHasReadableDescription() {
         assertDescription("map containing [ANYTHING->\"a\"]", hasValue("a"));
     }
 
+    @Test
     public void testDoesNotMatchEmptyMap() {
         Map map = new HashMap<>();
         assertMismatchDescription("map was []", hasValue(1), map);
     }
 
+    @Test
     public void testMatchesSingletonMapContainingValue() {
         Map map = new HashMap<>();
         map.put("a", 1);
@@ -32,6 +37,7 @@ public void testMatchesSingletonMapContainingValue() {
         assertMatches("Singleton map", hasValue(1), map);
     }
 
+    @Test
     public void testMatchesMapContainingValue() {
         Map map = new TreeMap<>();
         map.put("a", 1);
diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsMapWithSizeTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsMapWithSizeTest.java
index bcd2761f..ebe6a570 100644
--- a/hamcrest/src/test/java/org/hamcrest/collection/IsMapWithSizeTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/collection/IsMapWithSizeTest.java
@@ -1,12 +1,14 @@
 package org.hamcrest.collection;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
 import org.hamcrest.MatcherAssert;
+import org.junit.jupiter.api.Test;
 
 import java.util.HashMap;
 import java.util.Map;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
 import static org.hamcrest.core.IsEqual.equalTo;
 
@@ -17,56 +19,66 @@ protected Matcher createMatcher() {
         return aMapWithSize(7);
     }
 
+    @Test
     public void testMatchesWhenSizeIsCorrect() {
         assertMatches("correct size", aMapWithSize(equalTo(2)), mapWithKeys("a", "b"));
         assertMismatchDescription("map size was <3>", aMapWithSize(equalTo(2)), mapWithKeys("a", "b", "c"));
     }
 
+    @Test
     public void testMatchesMapWhenSizeIsCorrectUsingObjectElementType() {
         Map map = mapWithKeys(new Object(), new Object());
         assertMatches("correct size", aMapWithSize(equalTo(2)), map);
         assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map);
     }
 
+    @Test
     public void testMatchesMapWhenSizeIsCorrectUsingStringElementType() {
         Map map = mapWithKeys("a", "b");
         assertMatches("correct size", aMapWithSize(equalTo(2)), map);
         assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map);
     }
 
+    @Test
     public void testMatchesMapWhenSizeIsCorrectUsingWildcardElementType() {
         Map map = mapWithKeys("a", "b");
         assertMatches("correct size", aMapWithSize(equalTo(2)), map);
         assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map);
     }
 
+    @Test
     public void testMatchesListWhenSizeIsCorrectUsingObjectElementType() {
         Map map = mapWithKeys(new Object(), new Object());
         assertMatches("correct size", aMapWithSize(equalTo(2)), map);
         assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map);
     }
 
+    @Test
     public void testMatchesListWhenSizeIsCorrectUsingStringElementType() {
         Map list = mapWithKeys("a", "b");
         assertMatches("correct size", aMapWithSize(equalTo(2)), list);
         assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), list);
     }
 
+    @Test
     public void testMatchesListWhenSizeIsCorrectUsingWildcardElementType() {
         Map list = mapWithKeys("a", "b");
         assertMatches("correct size", aMapWithSize(equalTo(2)), list);
         assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), list);
     }
 
+    @Test
     public void testProvidesConvenientShortcutForHasSizeEqualTo() {
         assertMatches("correct size", aMapWithSize(2), mapWithKeys(new Object(), new Object()));
         assertMismatchDescription("map size was <3>", aMapWithSize(2), mapWithKeys(new Object(), new Object(), new Object()));
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("a map with size <3>", aMapWithSize(equalTo(3)));
     }
 
+    @Test
     public void testCompilesWithATypedMap() {
       Map arrayList = new HashMap<>();
       MatcherAssert.assertThat(arrayList, aMapWithSize(0));
diff --git a/hamcrest/src/test/java/org/hamcrest/comparator/ComparatorMatcherBuilderTest.java b/hamcrest/src/test/java/org/hamcrest/comparator/ComparatorMatcherBuilderTest.java
index dfe3e4fa..9fe58976 100644
--- a/hamcrest/src/test/java/org/hamcrest/comparator/ComparatorMatcherBuilderTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/comparator/ComparatorMatcherBuilderTest.java
@@ -1,12 +1,15 @@
 package org.hamcrest.comparator;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.math.BigDecimal;
 import java.util.Comparator;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.assertDescription;
+import static org.hamcrest.test.MatcherAssertions.assertMismatchDescription;
 import static org.hamcrest.comparator.ComparatorMatcherBuilder.comparedBy;
 import static org.hamcrest.core.IsNot.not;
 
@@ -33,6 +36,7 @@ protected Matcher createMatcher() {
         return integerComparatorMatcherBuilder.greaterThan(1);
     }
 
+    @Test
     public void testDescription() {
         assertDescription("a value greater than <1>", integerComparatorMatcherBuilder.greaterThan(1));
         assertDescription("a value equal to or greater than <1>", integerComparatorMatcherBuilder.greaterThanOrEqualTo(1));
@@ -47,6 +51,7 @@ public void testDescription() {
         assertDescription("a value less than <1> when compared by ", comparedBy(backwardsIntegerComparator).lessThan(1));
     }
 
+    @Test
     public void testMismatchDescriptions() {
         assertMismatchDescription("<0> was less than <1>", integerComparatorMatcherBuilder.greaterThan(1), 0);
         assertMismatchDescription("<1> was equal to <1>", integerComparatorMatcherBuilder.greaterThan(1), 1);
@@ -59,45 +64,54 @@ public void testMismatchDescriptions() {
         assertMismatchDescription("<2> was equal to <2> when compared by ", comparedBy(backwardsIntegerComparator).lessThan(2), 2);
     }
 
+    @Test
     public void testComparesObjectsForGreaterThan() {
         assertThat(2, integerComparatorMatcherBuilder.greaterThan(1));
         assertThat(0, not(integerComparatorMatcherBuilder.greaterThan(1)));
     }
 
+    @Test
     public void testComparesObjectsForLessThan() {
         assertThat(2, integerComparatorMatcherBuilder.lessThan(3));
         assertThat(0, integerComparatorMatcherBuilder.lessThan(1));
     }
 
+    @Test
     public void testComparesObjectsForEquality() {
         assertThat(3, integerComparatorMatcherBuilder.comparesEqualTo(3));
         assertThat("aa", stringComparatorMatcherBuilder.comparesEqualTo("aa"));
     }
 
+    @Test
     public void testAllowsForInclusiveComparisons() {
         assertThat("less", 1, integerComparatorMatcherBuilder.lessThanOrEqualTo(1));
         assertThat("greater", 1, integerComparatorMatcherBuilder.greaterThanOrEqualTo(1));
     }
 
+    @Test
     public void testSupportsDifferentTypesOfComparableObjects() {
         assertThat(1.1, doubleComparatorMatcherBuilder.greaterThan(1.0));
         assertThat("cc", stringComparatorMatcherBuilder.greaterThan("bb"));
     }
 
+    @Test
     public void testComparesBigDecimalsWithDifferentScalesCorrectlyForIssue20() {
         assertThat(new BigDecimal("10.0"), bigDecimalComparatorMatcherBuilder.greaterThanOrEqualTo(new BigDecimal("10")));
         assertThat(new BigDecimal(10), bigDecimalComparatorMatcherBuilder.greaterThanOrEqualTo(new BigDecimal("10.0")));
         assertThat(new BigDecimal("2"), bigDecimalComparatorMatcherBuilder.comparesEqualTo(new BigDecimal("2.000")));
     }
 
+    @Test
     public void testComparesCustomTypesWhoseCompareToReturnsValuesGreaterThatOne() {
         assertThat(new CustomInt(5), ComparatorMatcherBuilder.usingNaturalOrdering().lessThan(new CustomInt(10)));
     }
 
+    @Test
     public void testComparesByCustomComparator() {
         assertThat(5, comparedBy(backwardsIntegerComparator).lessThan(4));
     }
 
+    @Test
     public void testJavadocExamples() {
         assertThat(1, ComparatorMatcherBuilder.usingNaturalOrdering().comparesEqualTo(1));
         assertThat(2, ComparatorMatcherBuilder.usingNaturalOrdering().greaterThan(1));
diff --git a/hamcrest/src/test/java/org/hamcrest/comparator/ComparatorMatcherTest.java b/hamcrest/src/test/java/org/hamcrest/comparator/ComparatorMatcherTest.java
index 42270cc7..772bc6a8 100644
--- a/hamcrest/src/test/java/org/hamcrest/comparator/ComparatorMatcherTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/comparator/ComparatorMatcherTest.java
@@ -1,12 +1,15 @@
 package org.hamcrest.comparator;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.math.BigDecimal;
 import java.util.Comparator;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.assertDescription;
+import static org.hamcrest.test.MatcherAssertions.assertMismatchDescription;
 import static org.hamcrest.core.IsNot.not;
 import static org.hamcrest.number.OrderingComparison.*;
 
@@ -22,6 +25,7 @@ public int compare(Integer o1, Integer o2) {
         }).greaterThan(1);
     }
 
+    @Test
     public void testDescription() {
         assertDescription("a value greater than <1>", greaterThan(1));
         assertDescription("a value equal to or greater than <1>", greaterThanOrEqualTo(1));
@@ -30,6 +34,7 @@ public void testDescription() {
         assertDescription("a value less than <1>", lessThan(1));
     }
 
+    @Test
     public void testMismatchDescriptions() {
         assertMismatchDescription("<0> was less than <1>", greaterThan(1), 0);
         assertMismatchDescription("<1> was equal to <1>", greaterThan(1), 1);
@@ -37,37 +42,44 @@ public void testMismatchDescriptions() {
         assertMismatchDescription("<2> was equal to <2>", lessThan(2), 2);
     }
 
+    @Test
     public void testComparesObjectsForGreaterThan() {
         assertThat(2, greaterThan(1));
         assertThat(0, not(greaterThan(1)));
     }
 
+    @Test
     public void testComparesObjectsForLessThan() {
         assertThat(2, lessThan(3));
         assertThat(0, lessThan(1));
     }
 
+    @Test
     public void testComparesObjectsForEquality() {
         assertThat(3, comparesEqualTo(3));
         assertThat("aa", comparesEqualTo("aa"));
     }
 
+    @Test
     public void testAllowsForInclusiveComparisons() {
         assertThat("less", 1, lessThanOrEqualTo(1));
         assertThat("greater", 1, greaterThanOrEqualTo(1));
     }
 
+    @Test
     public void testSupportsDifferentTypesOfComparableObjects() {
         assertThat(1.1, greaterThan(1.0));
         assertThat("cc", greaterThan("bb"));
     }
 
+    @Test
     public void testComparesBigDecimalsWithDifferentScalesCorrectlyForIssue20() {
         assertThat(new BigDecimal("10.0"), greaterThanOrEqualTo(new BigDecimal("10")));
         assertThat(new BigDecimal(10), greaterThanOrEqualTo(new BigDecimal("10.0")));
         assertThat(new BigDecimal("2"), comparesEqualTo(new BigDecimal("2.000")));
     }
 
+    @Test
     public void testComparesCustomTypesWhoseCompareToReturnsValuesGreaterThatOne() {
         assertThat(new CustomInt(5), lessThan(new CustomInt(10)));
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/core/AllOfTest.java b/hamcrest/src/test/java/org/hamcrest/core/AllOfTest.java
index 892a94ef..e174d66d 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/AllOfTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/AllOfTest.java
@@ -1,10 +1,10 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.AllOf.allOf;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsEqual.equalTo;
diff --git a/hamcrest/src/test/java/org/hamcrest/core/AnyOfTest.java b/hamcrest/src/test/java/org/hamcrest/core/AnyOfTest.java
index 2cf7c2a8..013eafba 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/AnyOfTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/AnyOfTest.java
@@ -1,10 +1,10 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.AnyOf.anyOf;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.hamcrest.core.StringEndsWith.endsWith;
diff --git a/hamcrest/src/test/java/org/hamcrest/core/CombinableTest.java b/hamcrest/src/test/java/org/hamcrest/core/CombinableTest.java
index aaa58053..56d968fe 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/CombinableTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/CombinableTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.hamcrest.core.IsNot.not;
 import static org.hamcrest.core.IsNull.notNullValue;
diff --git a/hamcrest/src/test/java/org/hamcrest/core/DescribedAsTest.java b/hamcrest/src/test/java/org/hamcrest/core/DescribedAsTest.java
index fffc83bc..fcd4e868 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/DescribedAsTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/DescribedAsTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.DescribedAs.describedAs;
 import static org.hamcrest.core.IsAnything.anything;
 import static org.hamcrest.core.IsEqual.equalTo;
diff --git a/hamcrest/src/test/java/org/hamcrest/core/EveryTest.java b/hamcrest/src/test/java/org/hamcrest/core/EveryTest.java
index 49f5f274..ac00e5bc 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/EveryTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/EveryTest.java
@@ -1,13 +1,13 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.StringContains.containsString;
 
 public final class EveryTest {
diff --git a/hamcrest/src/test/java/org/hamcrest/core/IsAnythingTest.java b/hamcrest/src/test/java/org/hamcrest/core/IsAnythingTest.java
index 80a8444c..7b6aaac9 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/IsAnythingTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/IsAnythingTest.java
@@ -1,11 +1,11 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.assertDescription;
-import static org.hamcrest.AbstractMatcherTest.assertMatches;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.assertDescription;
+import static org.hamcrest.test.MatcherAssertions.assertMatches;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsAnything.anything;
 
diff --git a/hamcrest/src/test/java/org/hamcrest/core/IsCollectionContainingTest.java b/hamcrest/src/test/java/org/hamcrest/core/IsCollectionContainingTest.java
index 4f32ca37..a528a1ef 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/IsCollectionContainingTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/IsCollectionContainingTest.java
@@ -1,9 +1,10 @@
 package org.hamcrest.core;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -11,6 +12,7 @@
 
 import static java.util.Arrays.asList;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.IsCollectionContaining.hasItem;
 import static org.hamcrest.core.IsCollectionContaining.hasItems;
 import static org.hamcrest.core.IsEqual.equalTo;
@@ -22,6 +24,7 @@ protected Matcher createMatcher() {
         return hasItem(equalTo("irrelevant"));
     }
 
+    @Test
     public void testMatchesACollectionThatContainsAnElementMatchingTheGivenMatcher() {
         Matcher> itemMatcher = hasItem(equalTo("a"));
 
@@ -29,6 +32,7 @@ public void testMatchesACollectionThatContainsAnElementMatchingTheGivenMatcher()
                 itemMatcher, asList("a", "b", "c"));
     }
 
+    @Test
     public void testDoesNotMatchCollectionThatDoesntContainAnElementMatchingTheGivenMatcher() {
         final Matcher> matcher1 = hasItem(mismatchable("a"));
         assertMismatchDescription("mismatches were: [mismatched: b, mismatched: c]", matcher1, asList("b", "c"));
@@ -37,14 +41,17 @@ public void testDoesNotMatchCollectionThatDoesntContainAnElementMatchingTheGiven
         assertMismatchDescription("was empty", matcher2, new ArrayList());
     }
 
+    @Test
     public void testDoesNotMatchNull() {
         assertDoesNotMatch("should not matches null", hasItem(equalTo("a")), null);
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("a collection containing \"a\"", hasItem(equalTo("a")));
     }
 
+    @Test
     public void testCanMatchItemWhenCollectionHoldsSuperclass() // Issue 24
     {
       final Set s = new HashSet<>();
@@ -53,6 +60,7 @@ public void testCanMatchItemWhenCollectionHoldsSuperclass() // Issue 24
       assertThat(s, IsCollectionContaining.hasItem(2));
     }
 
+    @Test
     @SuppressWarnings("unchecked")
     public void testMatchesAllItemsInCollection() {
         final Matcher> matcher1 = hasItems(equalTo("a"), equalTo("b"), equalTo("c"));
diff --git a/hamcrest/src/test/java/org/hamcrest/core/IsEqualTest.java b/hamcrest/src/test/java/org/hamcrest/core/IsEqualTest.java
index 218c86e3..f8eb1a24 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/IsEqualTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/IsEqualTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.hamcrest.core.IsEqual.equalToObject;
 
diff --git a/hamcrest/src/test/java/org/hamcrest/core/IsInstanceOfTest.java b/hamcrest/src/test/java/org/hamcrest/core/IsInstanceOfTest.java
index df367b2f..e7c9f60b 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/IsInstanceOfTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/IsInstanceOfTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.IsInstanceOf.any;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
 
diff --git a/hamcrest/src/test/java/org/hamcrest/core/IsIterableContainingTest.java b/hamcrest/src/test/java/org/hamcrest/core/IsIterableContainingTest.java
index 2f5eb5b3..a855c179 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/IsIterableContainingTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/IsIterableContainingTest.java
@@ -3,7 +3,7 @@
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeDiagnosingMatcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -12,7 +12,7 @@
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.singleton;
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.hamcrest.core.IsIterableContaining.hasItem;
diff --git a/hamcrest/src/test/java/org/hamcrest/core/IsNotTest.java b/hamcrest/src/test/java/org/hamcrest/core/IsNotTest.java
index a9160a52..fe1d2d22 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/IsNotTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/IsNotTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.hamcrest.core.IsNot.not;
diff --git a/hamcrest/src/test/java/org/hamcrest/core/IsNullTest.java b/hamcrest/src/test/java/org/hamcrest/core/IsNullTest.java
index c407696c..c4062740 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/IsNullTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/IsNullTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.hamcrest.core.IsNull.nullValue;
 
diff --git a/hamcrest/src/test/java/org/hamcrest/core/IsSameTest.java b/hamcrest/src/test/java/org/hamcrest/core/IsSameTest.java
index 35b8bad3..6efa1787 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/IsSameTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/IsSameTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.IsSame.sameInstance;
 import static org.hamcrest.core.IsSame.theInstance;
 
diff --git a/hamcrest/src/test/java/org/hamcrest/core/IsTest.java b/hamcrest/src/test/java/org/hamcrest/core/IsTest.java
index 8d5f9b72..6c4a8315 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/IsTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/IsTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.core;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.Is.isA;
 import static org.hamcrest.core.IsEqual.equalTo;
diff --git a/hamcrest/src/test/java/org/hamcrest/core/StringContainsTest.java b/hamcrest/src/test/java/org/hamcrest/core/StringContainsTest.java
index 6bd9da71..56161f8a 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/StringContainsTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/StringContainsTest.java
@@ -1,8 +1,10 @@
 package org.hamcrest.core;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.StringContains.containsString;
 import static org.hamcrest.core.StringContains.containsStringIgnoringCase;
 
@@ -16,6 +18,7 @@ protected Matcher createMatcher() {
         return stringContains;
     }
 
+    @Test
     public void testMatchesSubstrings() {
         assertMatches(stringContains, EXCERPT + "END");
         assertMatches(stringContains, "START" + EXCERPT);
@@ -29,6 +32,7 @@ public void testMatchesSubstrings() {
         assertDescription("a string containing \"EXCERPT\"", stringContains);
     }
 
+    @Test
     public void testMatchesSubstringsIgnoringCase() {
         final Matcher ignoringCase = containsStringIgnoringCase("ExCert");
         assertMatches(ignoringCase, "eXcERT" + "END");
diff --git a/hamcrest/src/test/java/org/hamcrest/core/StringEndsWithTest.java b/hamcrest/src/test/java/org/hamcrest/core/StringEndsWithTest.java
index 71533215..bfad819c 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/StringEndsWithTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/StringEndsWithTest.java
@@ -1,8 +1,10 @@
 package org.hamcrest.core;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.StringEndsWith.endsWith;
 import static org.hamcrest.core.StringEndsWith.endsWithIgnoringCase;
 
@@ -16,6 +18,7 @@ protected Matcher createMatcher() {
         return stringEndsWith;
     }
 
+    @Test
     public void testMatchesSubstringAtEnd() {
         assertDoesNotMatch(stringEndsWith, EXCERPT + "END");
         assertMatches(stringEndsWith, "START" + EXCERPT);
@@ -29,6 +32,7 @@ public void testMatchesSubstringAtEnd() {
         assertDescription("a string ending with \"EXCERPT\"", stringEndsWith);
     }
 
+    @Test
     public void testMatchesSubstringAtEndIngoringCase() {
         final Matcher ignoringCase = endsWithIgnoringCase("EXCERpt");
         assertDoesNotMatch(ignoringCase, "eXCErpt" + "END");
diff --git a/hamcrest/src/test/java/org/hamcrest/core/StringMatchingTest.java b/hamcrest/src/test/java/org/hamcrest/core/StringMatchingTest.java
index 12049a61..f93afbc0 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/StringMatchingTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/StringMatchingTest.java
@@ -1,36 +1,36 @@
 package org.hamcrest.core;
 
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
+import org.junit.jupiter.api.Test;
 
 import static org.hamcrest.core.StringContains.containsString;
 import static org.hamcrest.core.StringEndsWith.endsWith;
 import static org.hamcrest.core.StringStartsWith.startsWith;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 /**
  * @author Steve Freeman 2016 http://www.hamcrest.com
  */
 public class StringMatchingTest {
 
-  @Rule public final ExpectedException thrown = ExpectedException.none();
-
   @Test public void
   startsWithFailsWithNullSubstring() {
-    thrown.expect(IllegalArgumentException.class);
-    startsWith(null);
+    assertThrows(IllegalArgumentException.class, () -> {
+      startsWith(null);
+    });
   }
 
   @Test public void
   endWithFailsWithNullSubstring() {
-    thrown.expect(IllegalArgumentException.class);
-    endsWith(null);
+    assertThrows(IllegalArgumentException.class, () -> {
+      endsWith(null);
+    });
   }
 
   @Test public void
   containsFailsWithNullSubstring() {
-    thrown.expect(IllegalArgumentException.class);
-    containsString(null);
+    assertThrows(IllegalArgumentException.class, () -> {
+      containsString(null);
+    });
   }
 
 }
diff --git a/hamcrest/src/test/java/org/hamcrest/core/StringRegularExpressionTest.java b/hamcrest/src/test/java/org/hamcrest/core/StringRegularExpressionTest.java
index 1581d59a..bbd130d0 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/StringRegularExpressionTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/StringRegularExpressionTest.java
@@ -1,8 +1,10 @@
 package org.hamcrest.core;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.StringRegularExpression.matchesRegex;
 
 /**
@@ -15,6 +17,7 @@ public class StringRegularExpressionTest extends AbstractMatcherTest {
   @Override
   protected Matcher createMatcher() { return matcher; }
 
+  @Test
   public void testMatchingRegex() {
     assertMatches(matcher, "12");
     assertDoesNotMatch(matcher, "abc");
diff --git a/hamcrest/src/test/java/org/hamcrest/core/StringStartsWithTest.java b/hamcrest/src/test/java/org/hamcrest/core/StringStartsWithTest.java
index fb762bfa..db698ab1 100644
--- a/hamcrest/src/test/java/org/hamcrest/core/StringStartsWithTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/core/StringStartsWithTest.java
@@ -1,8 +1,10 @@
 package org.hamcrest.core;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.StringStartsWith.startsWith;
 import static org.hamcrest.core.StringStartsWith.startsWithIgnoringCase;
 
@@ -16,6 +18,7 @@ protected Matcher createMatcher() {
         return stringStartsWith;
     }
 
+    @Test
     public void testMatchesStringAtStart() {
         assertMatches(stringStartsWith, EXCERPT + "END");
         assertDoesNotMatch(stringStartsWith, "START" + EXCERPT);
@@ -29,6 +32,7 @@ public void testMatchesStringAtStart() {
         assertMismatchDescription("was \"Something else\"", stringStartsWith, "Something else");
     }
 
+    @Test
     public void testMatchesStringAtStartIgnoringCase() {
         final Matcher ignoreCase = startsWithIgnoringCase("EXCerPT");
 
diff --git a/hamcrest/src/test/java/org/hamcrest/exception/ThrowsExceptionTest.java b/hamcrest/src/test/java/org/hamcrest/exception/ThrowsExceptionTest.java
new file mode 100644
index 00000000..3fd3f51f
--- /dev/null
+++ b/hamcrest/src/test/java/org/hamcrest/exception/ThrowsExceptionTest.java
@@ -0,0 +1,90 @@
+package org.hamcrest.exception;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.throwsExceptionWithMessage;
+import static org.hamcrest.exception.ThrowsException.throwsException;
+import static org.hamcrest.test.MatcherAssertions.*;
+
+public final class ThrowsExceptionTest {
+
+  public static void throwIllegalArgumentException() {
+    throw new IllegalArgumentException("Boom!");
+  }
+
+  public static void throwNullPointerException() {
+    throw new NullPointerException("Boom!");
+  }
+
+  @Test
+  public void examples() {
+    assertThat(ThrowsExceptionTest::throwIllegalArgumentException, throwsException());
+    assertThat(ThrowsExceptionTest::throwIllegalArgumentException, throwsException(RuntimeException.class));
+    assertThat(ThrowsExceptionTest::throwIllegalArgumentException, throwsException(RuntimeException.class, "Boom!"));
+    assertThat(ThrowsExceptionTest::throwIllegalArgumentException, throwsException(new IllegalArgumentException("Boom!")));
+    assertThat(ThrowsExceptionTest::throwIllegalArgumentException, throwsException(RuntimeException.class, containsString("Boo")));
+    assertThat(ThrowsExceptionTest::throwIllegalArgumentException, throwsExceptionWithMessage("Boom!"));
+    assertThat(ThrowsExceptionTest::throwIllegalArgumentException, throwsExceptionWithMessage(containsString("Boo")));
+  }
+
+  @Test
+  public void evaluatesToTrueIfRunnableThrowsExpectedExceptionWithMatchingMessage() {
+    assertMatches(
+        throwsException(IllegalArgumentException.class, "Boom!"),
+        ThrowsExceptionTest::throwIllegalArgumentException
+    );
+
+    assertDescription(
+        "a runnable throwing an instance of java.lang.IllegalArgumentException with message \"Boom!\"",
+        throwsException(IllegalArgumentException.class, "Boom!")
+    );
+
+    assertMismatchDescription(
+        "thrown exception message was \"Boom!\"",
+        throwsException(IllegalArgumentException.class, "Bang!"),
+        (Runnable) ThrowsExceptionTest::throwIllegalArgumentException
+    );
+
+    assertMismatchDescription(
+        "thrown exception class was java.lang.NullPointerException",
+        throwsException(IllegalArgumentException.class, "Boom!"),
+        (Runnable) ThrowsExceptionTest::throwNullPointerException
+    );
+
+    assertMismatchDescription(
+        "the runnable didn't throw",
+        throwsException(IllegalArgumentException.class, "Boom!"),
+        (Runnable) () -> {
+        }
+    );
+  }
+
+  @Test
+  public void evaluatesToTrueIfRunnableThrowsExceptionExtendingTheExpectedExceptionWithMatchingMessage() {
+    assertMatches(
+        throwsException(IllegalArgumentException.class, "Boom!"),
+        ThrowsExceptionTest::throwIllegalArgumentException
+    );
+  }
+
+  @Test
+  public void evaluatesToTrueIfRunnableThrowsExceptionWithMatchingMessage() {
+    assertMatches(
+        throwsException(IllegalArgumentException.class, containsString("Boo")),
+        ThrowsExceptionTest::throwIllegalArgumentException
+    );
+
+    assertDescription(
+        "a runnable throwing an instance of java.lang.IllegalArgumentException with message a string containing \"Boo\"",
+        throwsException(IllegalArgumentException.class, containsString("Boo"))
+    );
+
+    assertMismatchDescription(
+        "thrown exception class was java.lang.NullPointerException",
+        throwsException(IllegalArgumentException.class, containsString("Boo")),
+        (Runnable) ThrowsExceptionTest::throwNullPointerException
+    );
+  }
+}
diff --git a/hamcrest/src/test/java/org/hamcrest/io/FileMatchersTest.java b/hamcrest/src/test/java/org/hamcrest/io/FileMatchersTest.java
index 18230743..0bdd71fb 100644
--- a/hamcrest/src/test/java/org/hamcrest/io/FileMatchersTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/io/FileMatchersTest.java
@@ -1,13 +1,17 @@
 package org.hamcrest.io;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 @SuppressWarnings("ResultOfMethodCallIgnored")
 public class FileMatchersTest extends AbstractMatcherTest {
@@ -15,31 +19,35 @@ public class FileMatchersTest extends AbstractMatcherTest {
     private File directory;
     private File file;
 
-    @Override
+    @BeforeEach
     protected void setUp() throws IOException {
         directory = Files.createTempDirectory("myDir").toFile();
         file = new File(directory, "myFile");
         file.createNewFile();
     }
 
+    @Test
     public void testAnExistingDirectory() {
         assertMatches("matches existing directory", FileMatchers.anExistingDirectory(), directory);
         assertDoesNotMatch("doesn't match existing file", FileMatchers.anExistingDirectory(), file);
         assertMismatchDescription("'foo' is not a directory", FileMatchers.anExistingDirectory(), new File("foo"));
     }
 
+    @Test
     public void testAnExistingFileOrDirectory() {
         assertMatches("matches existing file", FileMatchers.anExistingFileOrDirectory(), file);
         assertMatches("matches existing directory", FileMatchers.anExistingFileOrDirectory(), directory);
         assertMismatchDescription("'foo' does not exist", FileMatchers.anExistingFileOrDirectory(), new File("foo"));
     }
 
+    @Test
     public void testAnExistingFile() {
         assertMatches("matches existing file", FileMatchers.anExistingFile(), file);
         assertDoesNotMatch("doesn't match existing directory", FileMatchers.anExistingFile(), directory);
         assertMismatchDescription("'foo' is not a file", FileMatchers.anExistingFile(), new File("foo"));
     }
 
+    @Test
     public void testAReadableFile() { // Not all OSes will allow setting readability so have to be forgiving here.
         file.setReadable(true);
         assertMatches("matches readable file", FileMatchers.aReadableFile(), file);
@@ -49,33 +57,39 @@ public void testAReadableFile() { // Not all OSes will allow setting readability
         }
     }
 
+    @Test
     public void testAWritableFile() {
         assertMatches("matches writable file", FileMatchers.aWritableFile(), file);
 
-        assertTrue("set writable off " + file, file.setWritable(false));
+        assertTrue(file.setWritable(false), "set writable off " + file);
         assertDoesNotMatch("doesn't match unwritable file", FileMatchers.aWritableFile(), file);
     }
 
+    @Test
     public void testAFileWithSizeLong() {
         assertMatches("matches file size", FileMatchers.aFileWithSize(0L), file);
         assertDoesNotMatch("doesn't match incorrect file size", FileMatchers.aFileWithSize(34L), file);
     }
 
+    @Test
     public void testAFileWithSizeMatcherOfLong() {
         assertMatches("matches file size", FileMatchers.aFileWithSize(equalTo(0L)), file);
         assertDoesNotMatch("doesn't match incorrect file size", FileMatchers.aFileWithSize(equalTo(23L)), file);
     }
 
+    @Test
     public void testAFileNamed() {
         assertMatches("matches file name", FileMatchers.aFileNamed(equalTo(file.getName())), file);
         assertDoesNotMatch("doesn't match incorrect file name", FileMatchers.aFileNamed(equalTo("foo")), file);
     }
 
+    @Test
     public void testAFileWithCanonicalPath() throws Exception {
         assertMatches("matches file canonical path", FileMatchers.aFileWithCanonicalPath(equalTo(file.getCanonicalPath())), file);
         assertDoesNotMatch("doesn't match incorrect canonical path", FileMatchers.aFileWithCanonicalPath(equalTo("foo")), file);
     }
 
+    @Test
     public void testAFileWithAbsolutePath() {
         assertMatches("matches file absolute path", FileMatchers.aFileWithAbsolutePath(equalTo(file.getAbsolutePath())), file);
         assertDoesNotMatch("doesn't match incorrect absolute path", FileMatchers.aFileWithAbsolutePath(equalTo("foo")), file);
diff --git a/hamcrest/src/test/java/org/hamcrest/number/BigDecimalCloseToTest.java b/hamcrest/src/test/java/org/hamcrest/number/BigDecimalCloseToTest.java
index dd0ea4ec..83bed3fe 100644
--- a/hamcrest/src/test/java/org/hamcrest/number/BigDecimalCloseToTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/number/BigDecimalCloseToTest.java
@@ -1,11 +1,14 @@
 package org.hamcrest.number;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.math.BigDecimal;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.number.BigDecimalCloseTo.closeTo;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class BigDecimalCloseToTest  extends AbstractMatcherTest {
 
@@ -17,6 +20,7 @@ protected Matcher createMatcher() {
     return closeTo(irrelevant, irrelevant);
   }
 
+  @Test
   public void testEvaluatesToTrueIfArgumentIsEqualToABigDecimalWithinSomeError() {
     assertTrue(matcher.matches(new BigDecimal("1.0")));
     assertTrue(matcher.matches(new BigDecimal("0.5")));
@@ -28,6 +32,7 @@ public void testEvaluatesToTrueIfArgumentIsEqualToABigDecimalWithinSomeError() {
     assertMismatchDescription("<0.0> differed by <0.5> more than delta <0.5>", matcher, new BigDecimal("0.0"));
   }
 
+  @Test
   public void testEvaluatesToTrueIfArgumentHasDifferentScale() {
     assertTrue(matcher.matches(new BigDecimal("1.000000")));
     assertTrue(matcher.matches(new BigDecimal("0.500000")));
@@ -39,6 +44,7 @@ public void testEvaluatesToTrueIfArgumentHasDifferentScale() {
     assertMismatchDescription("<0.000000> differed by <0.5> more than delta <0.5>", matcher, new BigDecimal("0.000000"));
   }
 
+  @Test
   public void test_is_self_describing() {
     assertDescription("a numeric value within <0.5> of <1.0>", matcher);
   }
diff --git a/hamcrest/src/test/java/org/hamcrest/number/IsCloseToTest.java b/hamcrest/src/test/java/org/hamcrest/number/IsCloseToTest.java
index d06822f6..4f90490e 100644
--- a/hamcrest/src/test/java/org/hamcrest/number/IsCloseToTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/number/IsCloseToTest.java
@@ -1,8 +1,10 @@
 package org.hamcrest.number;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.number.IsCloseTo.closeTo;
 
 public class IsCloseToTest extends AbstractMatcherTest {
@@ -15,6 +17,7 @@ protected Matcher createMatcher() {
         return closeTo(irrelevant, irrelevant);
     }
 
+    @Test
     public void test_matchesIfArgumentIsEqualToADoubleValueWithinSomeError() {
         assertMatches("1.0", matcher, 1.0);
         assertMatches("0.5d", matcher, 0.5d);
@@ -26,6 +29,7 @@ public void test_matchesIfArgumentIsEqualToADoubleValueWithinSomeError() {
         assertMismatchDescription("<0.1> differed by <0.4> more than delta <0.5>", matcher, 0.1);
     }
 
+    @Test
     public void test_is_self_describing() {
         assertDescription("a numeric value within <0.5> of <1.0>", matcher);
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/number/IsNanTest.java b/hamcrest/src/test/java/org/hamcrest/number/IsNanTest.java
index 8cd6b759..5787286f 100644
--- a/hamcrest/src/test/java/org/hamcrest/number/IsNanTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/number/IsNanTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.number;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.number.IsNaN.notANumber;
 
 public final class IsNanTest {
diff --git a/hamcrest/src/test/java/org/hamcrest/number/OrderingComparisonTest.java b/hamcrest/src/test/java/org/hamcrest/number/OrderingComparisonTest.java
index 41044b22..60150c1a 100644
--- a/hamcrest/src/test/java/org/hamcrest/number/OrderingComparisonTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/number/OrderingComparisonTest.java
@@ -1,11 +1,14 @@
 package org.hamcrest.number;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.math.BigDecimal;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.assertDescription;
+import static org.hamcrest.test.MatcherAssertions.assertMismatchDescription;
 import static org.hamcrest.core.IsNot.not;
 import static org.hamcrest.number.OrderingComparison.*;
 
@@ -16,6 +19,7 @@ protected Matcher createMatcher() {
         return greaterThan(1);
     }
 
+    @Test
     public void testDescription() {
       assertDescription("a value greater than <1>", greaterThan(1));
       assertDescription("a value equal to or greater than <1>", greaterThanOrEqualTo(1));
@@ -24,6 +28,7 @@ public void testDescription() {
       assertDescription("a value less than <1>", lessThan(1));
     }
 
+    @Test
     public void testMismatchDescriptions() {
       assertMismatchDescription("<0> was less than <1>", greaterThan(1), 0);
       assertMismatchDescription("<1> was equal to <1>", greaterThan(1), 1);
@@ -31,37 +36,44 @@ public void testMismatchDescriptions() {
       assertMismatchDescription("<2> was equal to <2>", lessThan(2), 2);
     }
 
+    @Test
     public void testComparesObjectsForGreaterThan() {
         assertThat(2, greaterThan(1));
         assertThat(0, not(greaterThan(1)));
     }
 
+    @Test
     public void testComparesObjectsForLessThan() {
         assertThat(2, lessThan(3));
         assertThat(0, lessThan(1));
     }
 
+    @Test
     public void testComparesObjectsForEquality() {
       assertThat(3, comparesEqualTo(3));
       assertThat("aa", comparesEqualTo("aa"));
     }
 
+    @Test
     public void testAllowsForInclusiveComparisons() {
         assertThat("less", 1, lessThanOrEqualTo(1));
         assertThat("greater", 1, greaterThanOrEqualTo(1));
     }
 
+    @Test
     public void testSupportsDifferentTypesOfComparableObjects() {
         assertThat(1.1, greaterThan(1.0));
         assertThat("cc", greaterThan("bb"));
     }
 
+    @Test
     public void testComparesBigDecimalsWithDifferentScalesCorrectlyForIssue20() {
       assertThat(new BigDecimal("10.0"), greaterThanOrEqualTo(new BigDecimal("10")));
       assertThat(new BigDecimal(10), greaterThanOrEqualTo(new BigDecimal("10.0")));
       assertThat(new BigDecimal("2"), comparesEqualTo(new BigDecimal("2.000")));
     }
 
+    @Test
     public void testComparesCustomTypesWhoseCompareToReturnsValuesGreaterThatOne() {
         assertThat(new CustomInt(5), lessThan(new CustomInt(10)));
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/object/HasEqualsValuesTest.java b/hamcrest/src/test/java/org/hamcrest/object/HasEqualsValuesTest.java
index 96be09cd..9a56394a 100644
--- a/hamcrest/src/test/java/org/hamcrest/object/HasEqualsValuesTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/object/HasEqualsValuesTest.java
@@ -1,7 +1,10 @@
 package org.hamcrest.object;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.test.MatcherAssertions.*;
 
 @SuppressWarnings("WeakerAccess")
 public class HasEqualsValuesTest extends AbstractMatcherTest {
@@ -14,22 +17,26 @@ protected Matcher createMatcher() {
         return WITH_PUBLIC_FIELDS_MATCHER;
     }
 
+    @Test
     public void test_describes_itself() {
         assertDescription(
                 "WithPublicFields has values [i: <666>, s: \"a string\", c: \"x\"]",
                 WITH_PUBLIC_FIELDS_MATCHER);
     }
 
+    @Test
     public void test_matches_equivalent_object() {
         assertMatches(WITH_PUBLIC_FIELDS_MATCHER, new WithPublicFields('x', 666, "a string"));
     }
 
+    @Test
     public void test_mismatches_on_first_field_inequality() {
         assertMismatchDescription(
                 "'s' was \"different\"",
                 WITH_PUBLIC_FIELDS_MATCHER, new WithPublicFields('x', 666, "different"));
     }
 
+    @Test
     public void test_mismatches_on_inherited_field() {
         assertMismatchDescription(
                 "'c' was \"y\"",
diff --git a/hamcrest/src/test/java/org/hamcrest/object/HasToStringTest.java b/hamcrest/src/test/java/org/hamcrest/object/HasToStringTest.java
index 0e63a186..be03ef3a 100644
--- a/hamcrest/src/test/java/org/hamcrest/object/HasToStringTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/object/HasToStringTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.object;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.hamcrest.object.HasToString.hasToString;
 
diff --git a/hamcrest/src/test/java/org/hamcrest/object/IsCompatibleTypeTest.java b/hamcrest/src/test/java/org/hamcrest/object/IsCompatibleTypeTest.java
index 33169e34..0098363e 100644
--- a/hamcrest/src/test/java/org/hamcrest/object/IsCompatibleTypeTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/object/IsCompatibleTypeTest.java
@@ -1,9 +1,11 @@
 package org.hamcrest.object;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.assertDescription;
 import static org.hamcrest.object.IsCompatibleType.typeCompatibleWith;
 
 public class IsCompatibleTypeTest extends AbstractMatcherTest {
@@ -28,26 +30,32 @@ protected Matcher createMatcher() {
         return typeCompatibleWith(BaseClass.class);
     }
 
+    @Test
     public void testMatchesSameClass() {
         assertThat(BaseClass.class, typeCompatibleWith(BaseClass.class));
     }
 
+    @Test
     public void testMatchesSameInterface() {
         assertThat(BaseInterface.class, typeCompatibleWith(BaseInterface.class));
     }
 
+    @Test
     public void testMatchesExtendedClass() {
         assertThat(ExtendedClass.class, typeCompatibleWith(BaseClass.class));
     }
 
+    @Test
     public void testMatchesClassImplementingInterface() {
         assertThat(ClassImplementingBaseInterface.class, typeCompatibleWith(BaseInterface.class));
     }
 
+    @Test
     public void testMatchesExtendedInterface() {
         assertThat(ExtendedInterface.class, typeCompatibleWith(BaseInterface.class));
     }
 
+    @Test
     public void testHasReadableDescription() {
         assertDescription("type < java.lang.Runnable", typeCompatibleWith(Runnable.class));
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/object/IsEventFromTest.java b/hamcrest/src/test/java/org/hamcrest/object/IsEventFromTest.java
index b5a6bd04..0a5f029c 100644
--- a/hamcrest/src/test/java/org/hamcrest/object/IsEventFromTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/object/IsEventFromTest.java
@@ -1,11 +1,13 @@
 package org.hamcrest.object;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import java.util.EventObject;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.assertMismatchDescription;
 import static org.hamcrest.object.IsEventFrom.eventFrom;
 
 public class IsEventFromTest extends AbstractMatcherTest {
@@ -15,6 +17,7 @@ protected Matcher createMatcher() {
         return eventFrom(null);
     }
 
+    @Test
     public void testEvaluatesToTrueIfArgumentIsAnEventObjectFiredByASpecifiedSource() {
         Object o = "Source";
         EventObject ev = new EventObject(o);
@@ -34,6 +37,7 @@ public DerivedEvent(Object source) {
         }
     }
 
+    @Test
     public void testCanTestForSpecificEventClasses() {
         Object o = new Object();
         DerivedEvent goodEv = new DerivedEvent(o);
diff --git a/hamcrest/src/test/java/org/hamcrest/object/MatchesPatternTest.java b/hamcrest/src/test/java/org/hamcrest/object/MatchesPatternTest.java
index c8ec40dd..14cd63b8 100644
--- a/hamcrest/src/test/java/org/hamcrest/object/MatchesPatternTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/object/MatchesPatternTest.java
@@ -2,12 +2,12 @@
 
 import org.hamcrest.Matcher;
 import org.hamcrest.text.MatchesPattern;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 import java.util.regex.Pattern;
 
-import static org.hamcrest.AbstractMatcherTest.*;
-import static org.junit.Assert.assertThat;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.test.MatcherAssertions.*;
 
 public class MatchesPatternTest {
 
diff --git a/hamcrest/src/test/java/org/hamcrest/optional/OptionalMatchersTest.java b/hamcrest/src/test/java/org/hamcrest/optional/OptionalMatchersTest.java
index 52b08564..3af6309c 100644
--- a/hamcrest/src/test/java/org/hamcrest/optional/OptionalMatchersTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/optional/OptionalMatchersTest.java
@@ -6,12 +6,12 @@
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.optional.OptionalEmpty.emptyOptional;
 import static org.hamcrest.optional.OptionalWithValue.optionalWithValue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.util.Optional;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 public class OptionalMatchersTest {
 
diff --git a/hamcrest/src/test/java/org/hamcrest/test/AbstractMatcherTest.java b/hamcrest/src/test/java/org/hamcrest/test/AbstractMatcherTest.java
new file mode 100644
index 00000000..4e25cc79
--- /dev/null
+++ b/hamcrest/src/test/java/org/hamcrest/test/AbstractMatcherTest.java
@@ -0,0 +1,23 @@
+package org.hamcrest.test;
+
+import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.test.MatcherAssertions.assertNullSafe;
+
+public abstract class AbstractMatcherTest {
+    /**
+     * Create an instance of the Matcher so some generic safety-net tests can be run on it.
+     */
+    protected abstract Matcher createMatcher();
+
+    @Test
+    public void testIsNullSafe() {
+        assertNullSafe(createMatcher());
+    }
+
+    @Test
+    public void testCopesWithUnknownTypes() {
+        createMatcher().matches(new UnknownType());
+    }
+}
diff --git a/hamcrest/src/test/java/org/hamcrest/test/MatcherAssertions.java b/hamcrest/src/test/java/org/hamcrest/test/MatcherAssertions.java
new file mode 100644
index 00000000..fc974d18
--- /dev/null
+++ b/hamcrest/src/test/java/org/hamcrest/test/MatcherAssertions.java
@@ -0,0 +1,65 @@
+package org.hamcrest.test;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.StringDescription;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public interface MatcherAssertions {
+
+    static  void assertMatches(Matcher matcher, T arg) {
+        assertMatches("Expected match, but mismatched", matcher, arg);
+    }
+
+    static  void assertMatches(String message, Matcher matcher, Object arg) {
+        if (!matcher.matches(arg)) {
+            fail(message + " because: '" + mismatchDescription(matcher, arg) + "'");
+        }
+    }
+
+    static  void assertDoesNotMatch(Matcher c, T arg) {
+        assertDoesNotMatch("Unexpected match", c, arg);
+    }
+
+    static  void assertDoesNotMatch(String message, Matcher c, T arg) {
+        assertFalse(c.matches(arg), message);
+    }
+
+    static void assertDescription(String expected, Matcher matcher) {
+        Description description = new StringDescription();
+        description.appendDescriptionOf(matcher);
+        assertEquals(expected, description.toString().trim(), "Expected description");
+    }
+
+    static  void assertMismatchDescription(String expected, Matcher matcher, Object arg) {
+        assertFalse(matcher.matches(arg), "Precondition: Matcher should not match item.");
+        assertEquals(expected, mismatchDescription(matcher, arg), "Expected mismatch description");
+    }
+
+    static void assertNullSafe(Matcher matcher) {
+        try {
+            matcher.matches(null);
+        } catch (Exception e) {
+            fail("Matcher was not null safe");
+        }
+    }
+
+    static void assertUnknownTypeSafe(Matcher matcher) {
+        try {
+            matcher.matches(new UnknownType());
+        } catch (Exception e) {
+            fail("Matcher was not unknown type safe, because: " + e);
+        }
+    }
+
+    static  String mismatchDescription(Matcher matcher, Object arg) {
+        Description description = new StringDescription();
+        matcher.describeMismatch(arg, description);
+        return description.toString().trim();
+    }
+}
+
+@SuppressWarnings("WeakerAccess")
+class UnknownType {
+}
diff --git a/hamcrest/src/test/java/org/hamcrest/text/CharSequenceLengthTest.java b/hamcrest/src/test/java/org/hamcrest/text/CharSequenceLengthTest.java
index a2dd8796..c838dd31 100644
--- a/hamcrest/src/test/java/org/hamcrest/text/CharSequenceLengthTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/text/CharSequenceLengthTest.java
@@ -1,8 +1,11 @@
 package org.hamcrest.text;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.assertMatches;
+import static org.hamcrest.test.MatcherAssertions.assertMismatchDescription;
 import static org.hamcrest.number.OrderingComparison.lessThan;
 import static org.hamcrest.text.CharSequenceLength.hasLength;
 
@@ -17,6 +20,7 @@ protected Matcher createMatcher() {
         return hasLength(4);
     }
 
+    @Test
     public void test_matchesExactLengthOf_CharSequence() {
         final Matcher matcher = hasLength(4);
         assertMatches(matcher, "aaaa");
@@ -25,6 +29,7 @@ public void test_matchesExactLengthOf_CharSequence() {
         assertMismatchDescription("length was <6>", matcher, "aaaaaa");
     }
 
+    @Test
     public void test_matchesRelativeLengthOf_CharSequence() {
         final Matcher matcher = hasLength(lessThan(4));
         assertMatches(matcher, "aaa");
diff --git a/hamcrest/src/test/java/org/hamcrest/text/IsBlankStringTest.java b/hamcrest/src/test/java/org/hamcrest/text/IsBlankStringTest.java
index 42ce320a..198e6d5d 100644
--- a/hamcrest/src/test/java/org/hamcrest/text/IsBlankStringTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/text/IsBlankStringTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.text;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.text.IsBlankString.blankOrNullString;
 import static org.hamcrest.text.IsBlankString.blankString;
 
diff --git a/hamcrest/src/test/java/org/hamcrest/text/IsEmptyStringTest.java b/hamcrest/src/test/java/org/hamcrest/text/IsEmptyStringTest.java
index d0f42a82..56b3abac 100644
--- a/hamcrest/src/test/java/org/hamcrest/text/IsEmptyStringTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/text/IsEmptyStringTest.java
@@ -1,9 +1,9 @@
 package org.hamcrest.text;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.text.IsEmptyString.emptyOrNullString;
 import static org.hamcrest.text.IsEmptyString.emptyString;
 
diff --git a/hamcrest/src/test/java/org/hamcrest/text/IsEqualCompressingWhiteSpaceTest.java b/hamcrest/src/test/java/org/hamcrest/text/IsEqualCompressingWhiteSpaceTest.java
index bbd8af73..02f1e1e7 100644
--- a/hamcrest/src/test/java/org/hamcrest/text/IsEqualCompressingWhiteSpaceTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/text/IsEqualCompressingWhiteSpaceTest.java
@@ -1,8 +1,10 @@
 package org.hamcrest.text;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.text.IsEqualCompressingWhiteSpace.equalToCompressingWhiteSpace;
 
 public class IsEqualCompressingWhiteSpaceTest extends AbstractMatcherTest {
@@ -14,34 +16,41 @@ protected Matcher createMatcher() {
         return matcher;
     }
 
+    @Test
     public void testPassesIfWordsAreSameButWhitespaceDiffers() {
         assertMatches(matcher, "Hello World how are we?");
         assertMatches(matcher, "   Hello World   how are \n\n\twe?");
     }
 
+    @Test
     public void testFailsIfTextOtherThanWhitespaceDiffers() {
         assertDoesNotMatch(matcher, "Hello PLANET how are we?");
         assertDoesNotMatch(matcher, "Hello World how are we");
     }
 
+    @Test
     public void testFailsIfWhitespaceIsAddedOrRemovedInMidWord() {
         assertDoesNotMatch(matcher, "HelloWorld how are we?");
         assertDoesNotMatch(matcher, "Hello Wo rld how are we?");
     }
 
+    @Test
     public void test_has_a_readable_mismatch() {
         assertMismatchDescription("was \"Hello World how are we \"", matcher, "Hello World how are we ");
     }
 
+    @Test
     public void testFailsIfMatchingAgainstNull() {
         assertDoesNotMatch(matcher, null);
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("a string equal to \" Hello World   how\\n are we? \" compressing white space",
                         matcher);
     }
 
+    @Test
     public void testPassesIfWhitespacesContainsNoBreakSpace() {
         assertMatches(matcher, "Hello" + ((char)160) + "World how are we?");
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/text/IsEqualIgnoringCaseTest.java b/hamcrest/src/test/java/org/hamcrest/text/IsEqualIgnoringCaseTest.java
index 15bdf94f..02dfd9a9 100644
--- a/hamcrest/src/test/java/org/hamcrest/text/IsEqualIgnoringCaseTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/text/IsEqualIgnoringCaseTest.java
@@ -1,9 +1,10 @@
 package org.hamcrest.text;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.text.IsEqualIgnoringCase.equalToIgnoringCase;
 
 public final class IsEqualIgnoringCaseTest {
@@ -41,9 +42,11 @@ public final class IsEqualIgnoringCaseTest {
         assertDoesNotMatch(matcher, null);
     }
 
-    @Test(expected=IllegalArgumentException.class) public void
+    @Test public void
     canOnlyBeConstructedAboutANonNullString() {
-        equalToIgnoringCase(null);
+        Assertions.assertThrows(IllegalArgumentException.class, () -> {
+            equalToIgnoringCase(null);
+        });
     }
 
     @Test public void
diff --git a/hamcrest/src/test/java/org/hamcrest/text/StringContainsInOrderTest.java b/hamcrest/src/test/java/org/hamcrest/text/StringContainsInOrderTest.java
index 64a54e64..8fcae26e 100644
--- a/hamcrest/src/test/java/org/hamcrest/text/StringContainsInOrderTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/text/StringContainsInOrderTest.java
@@ -1,9 +1,11 @@
 package org.hamcrest.text;
 
-import org.hamcrest.AbstractMatcherTest;
+import org.hamcrest.test.AbstractMatcherTest;
 import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
 
 import static java.util.Arrays.asList;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.text.StringContainsInOrder.stringContainsInOrder;
 
 public class StringContainsInOrderTest extends AbstractMatcherTest {
@@ -15,6 +17,7 @@ protected Matcher createMatcher() {
         return matcher;
     }
 
+    @Test
     public void testMatchesOnlyIfStringContainsGivenSubstringsInTheSameOrder() {
         assertMatches("substrings in order", matcher, "abcc");
         assertMatches("substrings separated", matcher, "1a2b3c4c5");
@@ -27,6 +30,7 @@ public void testMatchesOnlyIfStringContainsGivenSubstringsInTheSameOrder() {
         assertDoesNotMatch("empty string", matcher, "");
     }
 
+    @Test
     public void testHasAReadableDescription() {
         assertDescription("a string containing \"a\", \"b\", \"c\", \"c\" in order", matcher);
     }
diff --git a/hamcrest/src/test/java/org/hamcrest/xml/HasXPathTest.java b/hamcrest/src/test/java/org/hamcrest/xml/HasXPathTest.java
index cc9b6316..c8f8b887 100644
--- a/hamcrest/src/test/java/org/hamcrest/xml/HasXPathTest.java
+++ b/hamcrest/src/test/java/org/hamcrest/xml/HasXPathTest.java
@@ -1,7 +1,7 @@
 package org.hamcrest.xml;
 
 import org.hamcrest.Matcher;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 
@@ -12,12 +12,12 @@
 import java.util.HashSet;
 import java.util.Iterator;
 
-import static org.hamcrest.AbstractMatcherTest.*;
+import static org.hamcrest.test.MatcherAssertions.*;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.hamcrest.core.IsNot.not;
 import static org.hamcrest.core.StringContains.containsString;
 import static org.hamcrest.xml.HasXPath.hasXPath;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.fail;
 
 /**
  * @author Joe Walnes
diff --git a/settings.gradle b/settings.gradle
index 1b9ee272..d4b00979 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,8 @@
+plugins {
+    // Apply the foojay-resolver plugin to allow automatic download of JDKs
+    id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
+}
+
 include 'hamcrest',
         'hamcrest-core',
         'hamcrest-library'