JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.
JUnit 4 integrates with Hamcrest matchers to provide expressive, readable assertions. Matchers allow you to write assertions in a more natural, English-like syntax and provide better error messages when assertions fail.
Use Hamcrest matchers with JUnit assertions for expressive test verification.
Note: As of JUnit 4.13, the assertThat methods in org.junit.Assert are deprecated. Users should migrate to using Hamcrest's MatcherAssert.assertThat() directly, or use third-party assertion libraries like AssertJ or Google Truth.
Import Consideration: When using Hamcrest matchers, avoid using both import static org.hamcrest.CoreMatchers.* and import static org.hamcrest.Matchers.* together, as many methods exist in both classes and will cause ambiguous reference errors. Use only one wildcard import (typically org.hamcrest.Matchers.* which includes all matchers).
/**
* Assert using Hamcrest matcher
* @param actual - Value to test
* @param matcher - Hamcrest matcher
* @deprecated Use {@link org.hamcrest.MatcherAssert#assertThat(Object, Matcher)} instead
*/
@Deprecated
public static <T> void assertThat(T actual, Matcher<? super T> matcher);
/**
* Assert using Hamcrest matcher with custom message
* @param reason - Message to display on failure
* @param actual - Value to test
* @param matcher - Hamcrest matcher
* @deprecated Use {@link org.hamcrest.MatcherAssert#assertThat(String, Object, Matcher)} instead
*/
@Deprecated
public static <T> void assertThat(String reason, T actual, Matcher<? super T> matcher);Usage Examples:
import org.junit.Test;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.*;
public class MatcherTest {
@Test
public void testWithMatchers() {
// Equality
assertThat(5, is(5));
assertThat("hello", equalTo("hello"));
// Negation
assertThat(5, not(3));
assertThat("hello", not("world"));
// Null checks
assertThat(null, nullValue());
assertThat("text", notNullValue());
// Type checks
assertThat("text", instanceOf(String.class));
assertThat(123, isA(Integer.class));
// String matchers
assertThat("hello world", containsString("world"));
assertThat("hello", startsWith("hel"));
assertThat("hello", endsWith("lo"));
// With custom message
assertThat("Value should be positive", 5, greaterThan(0));
}
@Test
public void testLogicalMatchers() {
// Any of (OR)
assertThat(5, either(is(5)).or(is(6)));
assertThat("test", anyOf(is("test"), is("demo"), is("example")));
// Both (AND)
assertThat(5, both(greaterThan(0)).and(lessThan(10)));
assertThat("test", allOf(startsWith("te"), endsWith("st")));
// Not
assertThat(5, not(lessThan(0)));
}
@Test
public void testCollectionMatchers() {
List<String> items = Arrays.asList("apple", "banana", "cherry");
// Has item
assertThat(items, hasItem("banana"));
assertThat(items, hasItems("apple", "cherry"));
// Collection properties
assertThat(items, not(empty()));
assertThat(items.size(), is(3));
// Array matchers
String[] array = {"a", "b", "c"};
assertThat(array, arrayContaining("a", "b", "c"));
assertThat(array, arrayContainingInAnyOrder("b", "a", "c"));
}
}JUnit-specific matcher utilities that extend Hamcrest matchers with additional functionality.
Note: Most methods in this class are deprecated. Use the equivalent methods from org.hamcrest.CoreMatchers instead. Only isThrowable and isException are not deprecated.
/**
* JUnit-specific Hamcrest matchers
*/
public class JUnitMatchers {
/**
* Matcher for collection containing specific element
* @param element - Element to find
* @return Matcher that matches collections containing element
* @deprecated use {@link org.hamcrest.CoreMatchers#hasItem(Object)}
*/
@Deprecated
public static <T> Matcher<Iterable<? super T>> hasItem(T element);
/**
* Matcher for collection containing element matching the given matcher
* @param elementMatcher - Matcher for the element
* @return Matcher that matches collections containing matching element
* @deprecated use {@link org.hamcrest.CoreMatchers#hasItem(Matcher)}
*/
@Deprecated
public static <T> Matcher<Iterable<? super T>> hasItem(Matcher<? super T> elementMatcher);
/**
* Matcher for collection containing all specified elements
* @param elements - Elements to find
* @return Matcher that matches collections containing all elements
* @deprecated use {@link org.hamcrest.CoreMatchers#hasItems(Object...)}
*/
@Deprecated
public static <T> Matcher<Iterable<T>> hasItems(T... elements);
/**
* Matcher for collection containing elements matching all given matchers
* @param elementMatchers - Matchers for elements
* @return Matcher that matches collections with matching elements
* @deprecated use {@link org.hamcrest.CoreMatchers#hasItems(Matcher...)}
*/
@Deprecated
public static <T> Matcher<Iterable<T>> hasItems(Matcher<? super T>... elementMatchers);
/**
* Matcher for collection where every element matches the given matcher
* @param elementMatcher - Matcher that each element must match
* @return Matcher that matches collections where all elements match
* @deprecated use {@link org.hamcrest.CoreMatchers#everyItem(Matcher)}
*/
@Deprecated
public static <T> Matcher<Iterable<T>> everyItem(Matcher<T> elementMatcher);
/**
* Matcher for string containing substring
* @param substring - Substring to find
* @return Matcher that matches strings containing substring
* @deprecated use {@link org.hamcrest.CoreMatchers#containsString(String)}
*/
@Deprecated
public static Matcher<String> containsString(String substring);
/**
* Combine two matchers with AND
* @param matcher - First matcher
* @return CombinableBothMatcher for chaining
* @deprecated use {@link org.hamcrest.CoreMatchers#both(Matcher)}
*/
@Deprecated
public static <T> CombinableBothMatcher<T> both(Matcher<? super T> matcher);
/**
* Combine two matchers with OR
* @param matcher - First matcher
* @return CombinableEitherMatcher for chaining
* @deprecated use {@link org.hamcrest.CoreMatchers#either(Matcher)}
*/
@Deprecated
public static <T> CombinableEitherMatcher<T> either(Matcher<? super T> matcher);
/**
* Matcher for throwable with matching cause, appending stacktrace on mismatch
* @param throwableMatcher - Matcher for the throwable
* @return Matcher for Throwable
*/
public static <T extends Throwable> Matcher<T> isThrowable(Matcher<T> throwableMatcher);
/**
* Matcher for exception with matching cause, appending stacktrace on mismatch
* @param exceptionMatcher - Matcher for the exception
* @return Matcher for Exception
*/
public static <T extends Exception> Matcher<T> isException(Matcher<T> exceptionMatcher);
}Usage Examples:
import org.junit.Test;
import org.junit.matchers.JUnitMatchers;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.*;
public class JUnitMatchersTest {
@Test
public void testStringMatcher() {
assertThat("hello world", JUnitMatchers.containsString("world"));
assertThat("hello world", JUnitMatchers.containsString("hello"));
}
@Test
public void testCombinedMatchers() {
// Both conditions must be true
assertThat(5, JUnitMatchers.both(greaterThan(0)).and(lessThan(10)));
// Either condition can be true
assertThat(5, JUnitMatchers.either(is(5)).or(is(10)));
}
@Test
public void testExceptionMatchers() {
Exception ex = new RuntimeException("Error", new IOException("IO failed"));
assertThat(ex, JUnitMatchers.isException(
isA(RuntimeException.class)
));
}
}Core matchers from Hamcrest library commonly used with JUnit.
// Object matchers
Matcher<T> equalTo(T operand);
Matcher<T> is(T value);
Matcher<T> not(T value);
Matcher<Object> nullValue();
Matcher<Object> notNullValue();
Matcher<T> sameInstance(T target);
Matcher<T> instanceOf(Class<?> type);
Matcher<T> any(Class<T> type);
// Comparable matchers
Matcher<T> greaterThan(T value);
Matcher<T> greaterThanOrEqualTo(T value);
Matcher<T> lessThan(T value);
Matcher<T> lessThanOrEqualTo(T value);
// String matchers
Matcher<String> containsString(String substring);
Matcher<String> startsWith(String prefix);
Matcher<String> endsWith(String suffix);
Matcher<String> equalToIgnoringCase(String expectedString);
Matcher<String> equalToIgnoringWhiteSpace(String expectedString);
Matcher<String> isEmptyString();
Matcher<String> isEmptyOrNullString();
// Collection matchers
Matcher<Collection<T>> hasItem(T item);
Matcher<Collection<T>> hasItems(T... items);
Matcher<Collection<T>> empty();
Matcher<Collection<T>> hasSize(int size);
Matcher<Iterable<T>> contains(T... items);
Matcher<Iterable<T>> containsInAnyOrder(T... items);
// Array matchers
Matcher<T[]> arrayContaining(T... items);
Matcher<T[]> arrayContainingInAnyOrder(T... items);
Matcher<T[]> arrayWithSize(int size);
Matcher<T[]> emptyArray();
// Logical matchers
Matcher<T> allOf(Matcher<? super T>... matchers);
Matcher<T> anyOf(Matcher<? super T>... matchers);
Matcher<T> not(Matcher<T> matcher);
// Number matchers
Matcher<Double> closeTo(double operand, double error);Usage Examples:
import org.junit.Test;
import static org.junit.Assert.assertThat;
import static org.hamcrest.Matchers.*;
public class HamcrestMatchersTest {
@Test
public void testObjectMatchers() {
assertThat(5, equalTo(5));
assertThat(5, is(5));
assertThat(null, nullValue());
assertThat("text", notNullValue());
assertThat("text", instanceOf(String.class));
assertThat("text", any(String.class));
String s1 = "hello";
String s2 = s1;
assertThat(s1, sameInstance(s2));
}
@Test
public void testComparableMatchers() {
assertThat(10, greaterThan(5));
assertThat(10, greaterThanOrEqualTo(10));
assertThat(5, lessThan(10));
assertThat(5, lessThanOrEqualTo(5));
}
@Test
public void testStringMatchers() {
assertThat("hello world", containsString("world"));
assertThat("hello world", startsWith("hello"));
assertThat("hello world", endsWith("world"));
assertThat("HELLO", equalToIgnoringCase("hello"));
assertThat("hello world", equalToIgnoringWhiteSpace("hello world"));
assertThat("", isEmptyString());
assertThat(null, isEmptyOrNullString());
}
@Test
public void testCollectionMatchers() {
List<String> items = Arrays.asList("apple", "banana", "cherry");
assertThat(items, hasItem("banana"));
assertThat(items, hasItems("apple", "cherry"));
assertThat(items, hasSize(3));
assertThat(items, not(empty()));
assertThat(items, contains("apple", "banana", "cherry"));
assertThat(items, containsInAnyOrder("cherry", "apple", "banana"));
}
@Test
public void testArrayMatchers() {
String[] array = {"a", "b", "c"};
assertThat(array, arrayContaining("a", "b", "c"));
assertThat(array, arrayContainingInAnyOrder("c", "a", "b"));
assertThat(array, arrayWithSize(3));
assertThat(new String[0], emptyArray());
}
@Test
public void testLogicalMatchers() {
// All conditions must be true
assertThat(5, allOf(greaterThan(0), lessThan(10), not(equalTo(7))));
// At least one condition must be true
assertThat(5, anyOf(equalTo(5), equalTo(10), equalTo(15)));
// Negation
assertThat(5, not(equalTo(10)));
assertThat(5, not(lessThan(0)));
}
@Test
public void testNumberMatchers() {
// Floating point comparison with tolerance
assertThat(3.14159, closeTo(3.14, 0.01));
assertThat(0.33333, closeTo(1.0/3.0, 0.001));
}
}Create custom matchers for domain-specific assertions.
/**
* Base class for custom matchers
*/
public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> {
/**
* Check if item matches
* @param item - Item to check
* @return true if matches
*/
protected abstract boolean matchesSafely(T item);
/**
* Describe the matcher
* @param description - Description to append to
*/
public abstract void describeTo(Description description);
/**
* Describe mismatch
* @param item - Item that didn't match
* @param mismatchDescription - Description to append to
*/
protected void describeMismatchSafely(T item, Description mismatchDescription);
}Usage Examples:
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.hamcrest.Matcher;
import org.junit.Test;
import static org.junit.Assert.assertThat;
// Custom matcher for even numbers
public class IsEven extends TypeSafeMatcher<Integer> {
@Override
protected boolean matchesSafely(Integer number) {
return number % 2 == 0;
}
@Override
public void describeTo(Description description) {
description.appendText("an even number");
}
@Override
protected void describeMismatchSafely(Integer item, Description mismatchDescription) {
mismatchDescription.appendValue(item).appendText(" is odd");
}
public static Matcher<Integer> even() {
return new IsEven();
}
}
// Custom matcher for valid email
public class IsValidEmail extends TypeSafeMatcher<String> {
@Override
protected boolean matchesSafely(String email) {
return email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$");
}
@Override
public void describeTo(Description description) {
description.appendText("a valid email address");
}
public static Matcher<String> validEmail() {
return new IsValidEmail();
}
}
// Custom matcher for range
public class InRange extends TypeSafeMatcher<Integer> {
private final int min;
private final int max;
public InRange(int min, int max) {
this.min = min;
this.max = max;
}
@Override
protected boolean matchesSafely(Integer value) {
return value >= min && value <= max;
}
@Override
public void describeTo(Description description) {
description.appendText("a value between ")
.appendValue(min)
.appendText(" and ")
.appendValue(max);
}
@Override
protected void describeMismatchSafely(Integer item, Description mismatchDescription) {
mismatchDescription.appendValue(item)
.appendText(" is outside range [")
.appendValue(min)
.appendText(", ")
.appendValue(max)
.appendText("]");
}
public static Matcher<Integer> inRange(int min, int max) {
return new InRange(min, max);
}
}
// Usage of custom matchers
public class CustomMatcherTest {
@Test
public void testEvenMatcher() {
assertThat(4, IsEven.even());
assertThat(2, IsEven.even());
// assertThat(3, IsEven.even()); // Fails with "3 is odd"
}
@Test
public void testEmailMatcher() {
assertThat("user@example.com", IsValidEmail.validEmail());
assertThat("test.user@domain.co.uk", IsValidEmail.validEmail());
// assertThat("invalid.email", IsValidEmail.validEmail()); // Fails
}
@Test
public void testRangeMatcher() {
assertThat(5, InRange.inRange(1, 10));
assertThat(1, InRange.inRange(1, 10));
assertThat(10, InRange.inRange(1, 10));
// assertThat(15, InRange.inRange(1, 10)); // Fails with "15 is outside range [1, 10]"
}
}
// Complex custom matcher with parameters
public class HasProperty<T> extends TypeSafeMatcher<T> {
private final String propertyName;
private final Matcher<?> valueMatcher;
public HasProperty(String propertyName, Matcher<?> valueMatcher) {
this.propertyName = propertyName;
this.valueMatcher = valueMatcher;
}
@Override
protected boolean matchesSafely(T obj) {
try {
String getterName = "get" + capitalize(propertyName);
Method getter = obj.getClass().getMethod(getterName);
Object value = getter.invoke(obj);
return valueMatcher.matches(value);
} catch (Exception e) {
return false;
}
}
@Override
public void describeTo(Description description) {
description.appendText("has property ")
.appendValue(propertyName)
.appendText(" matching ")
.appendDescriptionOf(valueMatcher);
}
public static <T> Matcher<T> hasProperty(String property, Matcher<?> matcher) {
return new HasProperty<>(property, matcher);
}
private String capitalize(String str) {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
}
// Usage
@Test
public void testPropertyMatcher() {
User user = new User("Alice", 25);
assertThat(user, HasProperty.hasProperty("name", equalTo("Alice")));
assertThat(user, HasProperty.hasProperty("age", greaterThan(18)));
}Combine matchers to create complex assertions.
Usage Examples:
import org.junit.Test;
import static org.junit.Assert.assertThat;
import static org.hamcrest.Matchers.*;
public class MatcherCompositionTest {
@Test
public void testComplexComposition() {
List<String> items = Arrays.asList("apple", "banana", "apricot", "avocado");
// All items start with 'a' and have length > 5
assertThat(items, everyItem(
allOf(
startsWith("a"),
hasLength(greaterThan(5))
)
));
// At least one item equals "banana"
assertThat(items, hasItem(equalTo("banana")));
// Collection has size 4 and contains "apple"
assertThat(items, allOf(
hasSize(4),
hasItem("apple")
));
}
@Test
public void testNestedMatchers() {
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 87);
scores.put("Charlie", 92);
assertThat(scores, allOf(
hasEntry("Alice", 95),
hasKey("Bob"),
hasValue(greaterThan(85))
));
}
@Test
public void testObjectPropertyMatchers() {
User user = new User("Alice", 25, "alice@example.com");
assertThat(user, allOf(
hasProperty("name", startsWith("A")),
hasProperty("age", both(greaterThan(18)).and(lessThan(100))),
hasProperty("email", containsString("@"))
));
}
}// Without matchers
assertTrue(result.size() > 0);
assertTrue(result.contains("expected"));
// With matchers - more readable
assertThat(result, not(empty()));
assertThat(result, hasItem("expected"));// Without matchers
// Expected: true but was: false
assertTrue(name.startsWith("Mr"));
// With matchers - more descriptive
// Expected: a string starting with "Mr" but: was "Ms Smith"
assertThat(name, startsWith("Mr"));// Multiple separate assertions
assertTrue(age >= 18);
assertTrue(age <= 65);
assertNotNull(name);
assertTrue(name.length() > 0);
// Single composed assertion
assertThat(age, allOf(greaterThanOrEqualTo(18), lessThanOrEqualTo(65)));
assertThat(name, allOf(notNullValue(), not(isEmptyString())));/**
* Base matcher interface
*/
public interface Matcher<T> extends SelfDescribing {
boolean matches(Object item);
void describeMismatch(Object item, Description mismatchDescription);
@Deprecated
void _dont_implement_Matcher___instead_extend_BaseMatcher_();
}
/**
* Base class for matchers
*/
public abstract class BaseMatcher<T> implements Matcher<T> {
@Override
public void describeMismatch(Object item, Description description);
@Override
public String toString();
}
/**
* Matcher description
*/
public interface Description {
Description appendText(String text);
Description appendDescriptionOf(SelfDescribing value);
Description appendValue(Object value);
Description appendList(String start, String separator, String end, Iterable<? extends SelfDescribing> values);
}
/**
* Combinable matcher for both/and
*/
public class CombinableBothMatcher<T> {
public CombinableMatcher<T> and(Matcher<? super T> matcher);
}
/**
* Combinable matcher for either/or
*/
public class CombinableEitherMatcher<T> {
public CombinableMatcher<T> or(Matcher<? super T> matcher);
}
/**
* Combined matcher result
*/
public class CombinableMatcher<T> extends TypeSafeMatcher<T> {
@Override
protected boolean matchesSafely(T item);
@Override
public void describeTo(Description description);
}Install with Tessl CLI
npx tessl i tessl/maven-junit--junit