net.jqwik.testing.ExpectFailure Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jqwik-testing Show documentation
Show all versions of jqwik-testing Show documentation
Jqwik Testing support module
package net.jqwik.testing;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;
import org.junit.platform.commons.support.*;
import net.jqwik.api.facades.*;
import net.jqwik.api.lifecycle.*;
import static net.jqwik.api.lifecycle.PropertyExecutionResult.Status.*;
/**
* Used to annotate methods that are expected to fail.
* Useful for testing jqwik itself
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AddLifecycleHook(ExpectFailure.Hook.class)
public @interface ExpectFailure {
class NullChecker implements Consumer {
@Override
public void accept(PropertyExecutionResult propertyExecutionResult) {
}
}
class NoFailure extends Throwable {
}
/**
* Optionally specify a checker
*/
Class extends Consumer> checkResult() default NullChecker.class;
String value() default "";
Class extends Throwable> failureType() default NoFailure.class;
class Hook implements AroundPropertyHook {
@Override
public PropertyExecutionResult aroundProperty(PropertyLifecycleContext context, PropertyExecutor property) throws Throwable {
PropertyExecutionResult testExecutionResult = property.execute();
Consumer resultChecker = getResultChecker(context.targetMethod(), context.testInstance());
String messageFromAnnotation = getMessage(context.targetMethod());
Class extends Throwable> expectedFailureType = getFailureType(context.targetMethod());
String headerText = messageFromAnnotation == null ? "" : messageFromAnnotation + "\n\t";
try {
if (testExecutionResult.status() == FAILED) {
Optional checkFailureResult = checkFailureType(expectedFailureType, testExecutionResult.throwable(), context.label(), headerText);
if (checkFailureResult.isPresent()) {
return testExecutionResult.mapToFailed(checkFailureResult.get());
}
resultChecker.accept(testExecutionResult);
return testExecutionResult.mapToSuccessful();
}
} catch (AssertionError assertionError) {
return testExecutionResult.mapToFailed(assertionError);
}
String message = createErrorMessage(context.label(), expectedFailureType, headerText, "it did not fail at all");
return testExecutionResult.mapToFailed(message);
}
private String createErrorMessage(
String label,
Class extends Throwable> expectedFailureType,
String headerText,
String reason
) {
String message = String.format(
"%sProperty [%s] should have failed with [%s], but %s",
headerText,
label,
expectedFailureType.getName(),
reason
);
return message;
}
private Optional checkFailureType(
Class extends Throwable> expectedFailureType,
Optional throwable,
String label,
String headerText
) {
if (expectedFailureType.equals(NoFailure.class)) {
return Optional.empty();
}
if (!throwable.isPresent()) {
String reason = "it failed without exception";
return Optional.of(createErrorMessage(label, expectedFailureType, headerText, reason));
}
if (!expectedFailureType.isAssignableFrom(throwable.get().getClass())) {
String reason = String.format("it failed with [%s]", throwable.get());
return Optional.of(createErrorMessage(label, expectedFailureType, headerText, reason));
}
return Optional.empty();
}
private String getMessage(Method method) {
Optional annotation = AnnotationSupport.findAnnotation(method, ExpectFailure.class);
return annotation.map(expectFailure -> {
String message = expectFailure.value();
return message.isEmpty() ? null : message;
}).orElse(null);
}
private Class extends Throwable> getFailureType(Method method) {
Optional annotation = AnnotationSupport.findAnnotation(method, ExpectFailure.class);
if (annotation.isPresent() && (annotation.get().failureType() != NoFailure.class)) {
return annotation.get().failureType();
} else {
return Throwable.class;
}
}
private Consumer getResultChecker(Method method, Object testInstance) {
Optional annotation = AnnotationSupport.findAnnotation(method, ExpectFailure.class);
return annotation.map((ExpectFailure expectFailure) -> {
Class extends Consumer> checkResult = expectFailure.checkResult();
return (Consumer) ReflectionSupportFacade.implementation
.newInstanceInTestContext(checkResult, testInstance);
}).orElse(
ReflectionSupportFacade.implementation.newInstanceInTestContext(NullChecker.class, testInstance)
);
}
@Override
public int aroundPropertyProximity() {
return -95;
}
}
}