All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jqwik.testing.ExpectFailure Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
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 org.opentest4j.*;

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> checkResult() default NullChecker.class;

	String value() default "";

	Class 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 expectedFailureType = getFailureType(context.targetMethod());

			try {
				if (testExecutionResult.status() == FAILED) {
					checkFailureType(expectedFailureType, testExecutionResult);
					resultChecker.accept(testExecutionResult);
					return testExecutionResult.mapToSuccessful();
				}
			} catch (AssertionError assertionError) {
				return testExecutionResult.mapToFailed(assertionError);
			}

			String headerText = messageFromAnnotation == null ? "" : messageFromAnnotation + "\n\t";
			String reason = testExecutionResult.throwable()
											   .map(throwable -> String.format("it failed with [%s]", throwable))
											   .orElse("it did not fail at all");
			String message = String.format(
					"%sProperty [%s] should have failed with failure of type %s, but %s",
					headerText,
					context.label(),
					expectedFailureType.getName(),
					reason
			);
			return testExecutionResult.mapToFailed(message);
		}

		private void checkFailureType(Class expectedFailureType, PropertyExecutionResult testExecutionResult) {
			if (expectedFailureType.equals(NoFailure.class)) {
				return;
			}
			testExecutionResult.throwable().ifPresent(throwable -> {
				if (!expectedFailureType.isAssignableFrom(throwable.getClass())) {
					throw new AssertionFailedError("Wrong failure type: " + throwable);
				}
			});
			if (!testExecutionResult.throwable().isPresent()) {
				throw new AssertionFailedError("No failure exception");
			}
		}

		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 getFailureType(Method method) {
			Optional annotation = AnnotationSupport.findAnnotation(method, ExpectFailure.class);
			if (annotation.isPresent()) {
				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> checkResult = expectFailure.checkResult();
				return (Consumer) ReflectionSupportFacade.implementation
																   .newInstanceInTestContext(checkResult, testInstance);
			}).orElse(
					ReflectionSupportFacade.implementation.newInstanceInTestContext(NullChecker.class, testInstance)
			);
		}

		@Override
		public int aroundPropertyProximity() {
			return -95;
		}

	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy