/* * Copyright (c) 2006 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.integration.junit5; import java.lang.reflect.*; import java.util.*; import javax.annotation.*; import org.junit.jupiter.api.extension.*; import mockit.*; import mockit.integration.internal.*; import mockit.internal.expectations.*; import mockit.internal.reflection.*; import mockit.internal.state.*; import static mockit.internal.util.StackTrace.*; @SuppressWarnings("Since15") final class JMockitExtension extends TestRunnerDecorator implements BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor, BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, ParameterResolver, TestExecutionExceptionHandler { @Nonnull private final Field indexField; @Nullable private SavePoint savePointForTestClass; @Nullable private SavePoint savePointForTest; @Nullable private SavePoint savePointForTestMethod; @Nullable private Throwable thrownByTest; @Nullable private Object[] mockParameters; JMockitExtension() { // Somehow, "Parameter#index" is not exposed in the Java API. try { indexField = Parameter.class.getDeclaredField("index"); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } } @Override public void beforeAll(@Nonnull ContainerExtensionContext context) { savePointForTestClass = new SavePoint(); @Nullable Class<?> testClass = context.getTestClass().orElse(null); TestRun.setCurrentTestClass(testClass); } @Override public void postProcessTestInstance(@Nonnull Object testInstance, @Nonnull ExtensionContext context) { TestRun.enterNoMockingZone(); try { handleMockFieldsForWholeTestClass(testInstance); } finally { TestRun.exitNoMockingZone(); } TestRun.setRunningIndividualTest(testInstance); } @Override public void beforeEach(@Nonnull TestExtensionContext context) { @Nonnull Object testInstance = context.getTestInstance(); TestRun.prepareForNextTest(); TestRun.enterNoMockingZone(); try { savePointForTest = new SavePoint(); createInstancesForTestedFields(testInstance, true); } finally { TestRun.exitNoMockingZone(); } } @Override public void beforeTestExecution(@Nonnull TestExtensionContext context) { Optional<Method> testMethod = context.getTestMethod(); if (!testMethod.isPresent()) { return; } @Nonnull Method method = testMethod.get(); @Nonnull Object testInstance = context.getTestInstance(); TestRun.enterNoMockingZone(); try { savePointForTestMethod = new SavePoint(); mockParameters = createInstancesForAnnotatedParameters(testInstance, method, null); createInstancesForTestedFields(testInstance, false); } finally { TestRun.exitNoMockingZone(); } TestRun.setRunningIndividualTest(testInstance); } @Override public boolean supports(@Nonnull ParameterContext parameterContext, @Nonnull ExtensionContext extensionContext) { @Nonnull Parameter parameter = parameterContext.getParameter(); return parameter.isAnnotationPresent(Tested.class) || parameter.isAnnotationPresent(Mocked.class) || parameter.isAnnotationPresent(Injectable.class) || parameter.isAnnotationPresent(Capturing.class); } @Override public Object resolve(@Nonnull ParameterContext parameterContext, @Nonnull ExtensionContext extensionContext) { @Nonnull Parameter parameter = parameterContext.getParameter(); Integer parameterIndex = FieldReflection.getFieldValue(indexField, parameter); //noinspection ConstantConditions return mockParameters[parameterIndex]; } @Override public void handleTestExecutionException( @Nonnull TestExtensionContext context, @Nonnull Throwable throwable) throws Throwable { thrownByTest = throwable; throw throwable; } @Override public void afterTestExecution(@Nonnull TestExtensionContext context) { TestRun.enterNoMockingZone(); try { assert savePointForTestMethod != null; savePointForTestMethod.rollback(); savePointForTestMethod = null; if (thrownByTest != null) { filterStackTrace(thrownByTest); } Error expectationsFailure = RecordAndReplayExecution.endCurrentReplayIfAny(); clearTestedObjectsIfAny(); if (expectationsFailure != null) { filterStackTrace(expectationsFailure); throw expectationsFailure; } } finally { TestRun.finishCurrentTestExecution(); TestRun.exitNoMockingZone(); } } @Override public void afterEach(@Nonnull TestExtensionContext context) { assert savePointForTest != null; savePointForTest.rollback(); savePointForTest = null; } @Override public void afterAll(@Nonnull ContainerExtensionContext context) { assert savePointForTestClass != null; savePointForTestClass.rollback(); savePointForTestClass = null; clearFieldTypeRedefinitions(); TestRun.setCurrentTestClass(null); } }