/* * Copyright (c) 2006 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.integration.junit4.internal; import java.lang.reflect.*; import javax.annotation.*; import org.junit.*; import org.junit.runners.model.*; import mockit.integration.internal.*; import mockit.internal.expectations.*; import mockit.internal.mockups.*; import mockit.internal.state.*; import static mockit.internal.util.StackTrace.*; final class JUnit4TestRunnerDecorator extends TestRunnerDecorator { @Nullable Object invokeExplosively(@Nonnull MockInvocation invocation, @Nullable Object target, Object... params) throws Throwable { FrameworkMethod it = invocation.getInvokedInstance(); // A @BeforeClass/@AfterClass method: if (target == null) { try { return executeClassMethod(invocation, params); } catch (Throwable t) { filterStackTrace(t); throw t; } } handleMockingOutsideTestMethods(target); // A @Before/@After method: if (it.getAnnotation(Test.class) == null) { if (shouldPrepareForNextTest && it.getAnnotation(Before.class) != null) { prepareToExecuteSetupMethod(target); } TestRun.setRunningIndividualTest(target); try { invocation.prepareToProceedFromNonRecursiveMock(); return it.invokeExplosively(target, params); } catch (Throwable t) { RecordAndReplayExecution.endCurrentReplayIfAny(); filterStackTrace(t); throw t; } finally { if (it.getAnnotation(After.class) != null) { shouldPrepareForNextTest = true; } } } if (shouldPrepareForNextTest) { prepareForNextTest(); } shouldPrepareForNextTest = true; try { executeTestMethod(invocation, target, params); return null; // it's a test method, therefore has void return type } catch (Throwable t) { filterStackTrace(t); throw t; } finally { TestRun.finishCurrentTestExecution(); } } @Nullable private Object executeClassMethod(@Nonnull MockInvocation inv, @Nonnull Object[] params) throws Throwable { FrameworkMethod method = inv.getInvokedInstance(); handleMockingOutsideTests(method); TestRun.clearCurrentTestInstance(); inv.prepareToProceedFromNonRecursiveMock(); return method.invokeExplosively(null, params); } private void prepareToExecuteSetupMethod(@Nonnull Object target) { discardTestLevelMockedTypes(); prepareForNextTest(); shouldPrepareForNextTest = false; createInstancesForTestedFields(target, true); } private void handleMockingOutsideTests(@Nonnull FrameworkMethod it) { Class<?> testClass = it.getMethod().getDeclaringClass(); TestRun.enterNoMockingZone(); try { Class<?> currentTestClass = TestRun.getCurrentTestClass(); if ( currentTestClass != null && testClass.isAssignableFrom(currentTestClass) && it.getAnnotation(AfterClass.class) != null ) { cleanUpMocksFromPreviousTest(); } if (it.getAnnotation(BeforeClass.class) != null) { updateTestClassState(null, testClass); } } finally { TestRun.exitNoMockingZone(); } } private void handleMockingOutsideTestMethods(@Nonnull Object target) { Class<?> testClass = target.getClass(); TestRun.enterNoMockingZone(); try { updateTestClassState(target, testClass); } finally { TestRun.exitNoMockingZone(); } } private static void executeTestMethod( @Nonnull MockInvocation invocation, @Nonnull Object target, @Nullable Object... parameters) throws Throwable { SavePoint savePoint = new SavePoint(); TestRun.setRunningIndividualTest(target); FrameworkMethod it = invocation.getInvokedInstance(); Method testMethod = it.getMethod(); Throwable testFailure = null; boolean testFailureExpected = false; try { Object[] annotatedParameters = createInstancesForAnnotatedParameters(target, testMethod, parameters); createInstancesForTestedFields(target, false); invocation.prepareToProceedFromNonRecursiveMock(); Object[] params = annotatedParameters == null ? parameters : annotatedParameters; it.invokeExplosively(target, params); } catch (Throwable thrownByTest) { testFailure = thrownByTest; Class<?> expectedType = testMethod.getAnnotation(Test.class).expected(); testFailureExpected = expectedType.isAssignableFrom(thrownByTest.getClass()); } finally { concludeTestMethodExecution(savePoint, testFailure, testFailureExpected); } } }