
uk.org.lidalia.test.Assert Maven / Gradle / Ivy
Show all versions of lidalia-test Show documentation
package uk.org.lidalia.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import uk.org.lidalia.lang.Modifier;
import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static uk.org.lidalia.lang.Exceptions.throwUnchecked;
/**
* Utility Hamcrest matchers for unit test assertions.
*/
public final class Assert {
/**
* Asserts that a class is not instantiable - it exists only for its static members.
*
* More precisely, asserts that the class:
*
* - Has Object as its immediate superclass
* - Has only one constructor
* - That constructor is private
* - That constructor takes no arguments
* - That constructor will throw an {@link UnsupportedOperationException} with message "Not Instantiable" if it is
* invoked via reflection.
*
*
* Usage:
* {@code assertThat(Values.class, isNotInstantiable());}
*
* @return a matcher that asserts that a class is not instantiable
*/
public static Matcher> isNotInstantiable() {
final Matcher isAThrowableWhoseMessageIs = is(aThrowableWhoseMessage(is("Not instantiable")));
final Matcher isAnUnsupportedOperationException = instanceOf(UnsupportedOperationException.class);
final CombinableMatcher bothIsAnUnsupportedOperationException = CombinableMatcher.both(
isAThrowableWhoseMessageIs);
final CombinableMatcher bothIsAnUnsupportedOperationExceptionAndIsAThrowableWhoseMessageIs =
bothIsAnUnsupportedOperationException
.and(isAnUnsupportedOperationException);
final Matcher aConstructorWhoseThrownExceptionBothIsAnUnsupportedOperationExceptionAndIsAThrowableWhoseMessageIs
= aConstructorWhoseThrownException(bothIsAnUnsupportedOperationExceptionAndIsAThrowableWhoseMessageIs);
final Matcher aMemberWithModifierPrivate = isAMemberWithModifier(Modifier.PRIVATE);
final Matcher isACollectionWhoseSizeIs0 = is(Assert.>>aCollectionWhoseSize(is(0)));
final Matcher isAConstructorWhoseParameterTypesAreACollectionWhoseSizeIs0 = is(aConstructorWhoseParameterTypes(isACollectionWhoseSizeIs0));
final CombinableMatcher> isAPrivateNoArgsConstructor = CombinableMatcher.both(
isAConstructorWhoseParameterTypesAreACollectionWhoseSizeIs0)
.and(aMemberWithModifierPrivate)
.and(aConstructorWhoseThrownExceptionBothIsAnUnsupportedOperationExceptionAndIsAThrowableWhoseMessageIs);
final Matcher> isEqualToObjectClass = is(equalTo(Object.class));
final FeatureMatcher, Class>> isAClassThatExtendsObjectDirectly = aClassWhoseSuperClass(isEqualToObjectClass);
final Matcher>> aSingleElementCollection = Assert.aCollectionWhoseSize(is(1));
final Matcher>> isASingleElementCollection = is(aSingleElementCollection);
final Matcher>> aListWhoseFirstElementIsAPrivateNoArgsConstructor = Assert.aListWhoseElementAtIndex(0, isAPrivateNoArgsConstructor);
final Matcher>> isAListWhoseFirstElementIsAPrivateNoArgsConstructor = is(aListWhoseFirstElementIsAPrivateNoArgsConstructor);
final CombinableMatcher>> both = CombinableMatcher.both(
isASingleElementCollection);
final CombinableMatcher>> isASinglePrivateNoArgsConstructor = both
.and(isAListWhoseFirstElementIsAPrivateNoArgsConstructor);
final FeatureMatcher, List>> isAClassWithASinglePrivateNoArgsConstructor = aClassWhoseSetOfConstructors(isASinglePrivateNoArgsConstructor);
final CombinableMatcher> both1 = CombinableMatcher.both(isAClassThatExtendsObjectDirectly);
final CombinableMatcher> and = both1.and(isAClassWithASinglePrivateNoArgsConstructor);
return (CombinableMatcher>) and;
}
/**
* Facilitates making an assertion about the superclass of a {@link Class}.
*
* Usage:
* {@code assertThat(String.class, is(aClassWhoseSuperClass(is(Object.class))));}
*
* @param classMatcher the matcher that will be applied to the class's superclass
* @return a matcher that will assert something about the superclass of a class
*/
public static FeatureMatcher, Class>> aClassWhoseSuperClass(
final Matcher extends Class>> classMatcher) {
return new FeatureMatcher, Class>>(
classMatcher, "a Class whose super class", "'s super class") {
@Override @SuppressWarnings("unchecked")
protected Class> featureValueOf(final Class> actual) {
return actual.getSuperclass();
}
};
}
private static FeatureMatcher, List>> aClassWhoseSetOfConstructors(
final Matcher>> matcher) {
return new FeatureMatcher, List>>(
matcher, "a Class whose set of constructors", "'s constructors") {
@Override
protected List> featureValueOf(final Class> actual) {
final List> constructors = asList(actual.getDeclaredConstructors());
Collections.sort(constructors, new Comparator>() {
@Override
public int compare(final Constructor> one, final Constructor> other) {
return one.toString().compareTo(other.toString());
}
});
return constructors;
}
};
}
/**
* Facilitates making an assertion about the size of a {@link Collection}.
*
* Usage:
* {@code assertThat(asList(1, 2, 3), is(aCollectionWhoseSize(is(3))));}
*
* @param sizeMatcher the matcher that will be applied to the collection's size
* @param the type of the collection whose size will be matched
* @return a matcher that will assert something about a collection's size
*/
public static > Matcher aCollectionWhoseSize(final Matcher sizeMatcher) {
return new FeatureMatcher(sizeMatcher, "a Collection whose size", "'s size") {
@Override
protected Integer featureValueOf(final T actual) {
return actual.size();
}
};
}
/**
* Facilitates making an assertion about the element at a given index of a {@link List}.
*
* Usage:
* {@code assertThat(asList("a", "b", "c"), is(aListWhoseElementAtIndex(2, is("c"))));}
*
* @param index the index of the element in the list the matcher will be applied to
* @param matcher the matcher that will be applied to the element
* @param the type of the elements in the List
* @return a matcher that will assert something about the element at the given index of a collection
*/
public static Matcher> aListWhoseElementAtIndex(
final Integer index, final Matcher matcher) {
return new FeatureMatcher, E>(matcher, "a List whose element at index " + index, "'s element at index " + index) {
@Override
protected E featureValueOf(final List extends E> actual) {
if (actual.size() > index) {
return actual.get(index);
} else {
throw new AssertionError(actual + " has no element at index " + index);
}
}
};
}
/**
* Asserts that a given {@link Member} has a given {@link Modifier}.
*
* Usage:
* {@code assertThat(Object.class.getMethod("toString"), isAMemberWithModifier(Modifier.PUBLIC));}
*
* @param modifier the modifier the member is expected to have
* @param the type of the Member
* @return a matcher that will assert a member has a modifier
*/
public static Matcher isAMemberWithModifier(final Modifier modifier) {
return new TypeSafeDiagnosingMatcher() {
@Override
protected boolean matchesSafely(final T item, final Description mismatchDescription) {
final boolean matches = modifier.existsOn(item);
if (!matches) {
mismatchDescription.appendValue(item).appendText(" did not have modifier ").appendValue(modifier);
}
return matches;
}
@Override
public void describeTo(final Description description) {
description.appendText("is a member with modifier " + modifier);
}
};
}
private static Matcher> aConstructorWhoseParameterTypes(final Matcher>> parameterMatcher) {
return new FeatureMatcher, List>>(
parameterMatcher, "a constructor whose parameter types", "'s parameter types") {
@Override
protected List> featureValueOf(final Constructor> actual) {
return asList(actual.getParameterTypes());
}
};
}
private static Matcher> aConstructorWhoseThrownException(final Matcher extends Throwable> throwableMatcher) {
return new FeatureMatcher, Throwable>(
throwableMatcher, "a constructor whose thrown exception", "'s thrown exception") {
@Override
protected Throwable featureValueOf(final Constructor> constructor) {
try {
constructor.setAccessible(true);
constructor.newInstance();
return null;
} catch (InvocationTargetException e) {
return e.getCause();
} catch (Exception e) {
return throwUnchecked(e, null);
} finally {
constructor.setAccessible(false);
}
}
};
}
private static Matcher aThrowableWhoseMessage(final Matcher messageMatcher) {
return new FeatureMatcher(messageMatcher, "a throwable whose message", "'s message") {
@Override
protected String featureValueOf(final Throwable actual) {
return actual.getMessage();
}
};
}
private Assert() {
throw new UnsupportedOperationException("Not instantiable");
}
}