View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
4    */
5   package mockit.integration.junit4;
6   
7   import static mockit.internal.util.StackTrace.filterStackTrace;
8   
9   import edu.umd.cs.findbugs.annotations.NonNull;
10  import edu.umd.cs.findbugs.annotations.Nullable;
11  
12  import java.lang.reflect.Method;
13  
14  import mockit.integration.TestRunnerDecorator;
15  import mockit.internal.expectations.RecordAndReplayExecution;
16  import mockit.internal.faking.FakeInvocation;
17  import mockit.internal.state.SavePoint;
18  import mockit.internal.state.TestRun;
19  
20  import org.junit.After;
21  import org.junit.AfterClass;
22  import org.junit.Before;
23  import org.junit.BeforeClass;
24  import org.junit.Test;
25  import org.junit.runners.model.FrameworkMethod;
26  
27  final class JUnit4TestRunnerDecorator extends TestRunnerDecorator {
28      @Nullable
29      Object invokeExplosively(@NonNull FakeInvocation invocation, @Nullable Object target, Object... params)
30              throws Throwable {
31          FrameworkMethod it = invocation.getInvokedInstance();
32          assert it != null;
33  
34          // A @BeforeClass/@AfterClass method:
35          if (target == null) {
36              try {
37                  return executeClassMethod(invocation, params);
38              } catch (Throwable t) {
39                  filterStackTrace(t);
40                  throw t;
41              }
42          }
43  
44          handleMockingOutsideTestMethods(target);
45  
46          // A @Before/@After method:
47          if (it.getAnnotation(Test.class) == null) {
48              if (shouldPrepareForNextTest && it.getAnnotation(Before.class) != null) {
49                  prepareToExecuteSetupMethod(target);
50              }
51  
52              TestRun.setRunningIndividualTest(target);
53  
54              try {
55                  invocation.prepareToProceedFromNonRecursiveMock();
56                  return it.invokeExplosively(target, params);
57              } catch (Throwable t) {
58                  // noinspection ThrowableNotThrown
59                  RecordAndReplayExecution.endCurrentReplayIfAny();
60                  filterStackTrace(t);
61                  throw t;
62              } finally {
63                  if (it.getAnnotation(After.class) != null) {
64                      shouldPrepareForNextTest = true;
65                  }
66              }
67          }
68  
69          if (shouldPrepareForNextTest) {
70              prepareForNextTest();
71          }
72  
73          shouldPrepareForNextTest = true;
74  
75          try {
76              executeTestMethod(invocation, target, params);
77              return null; // it's a test method, therefore has void return type
78          } catch (Throwable t) {
79              filterStackTrace(t);
80              throw t;
81          } finally {
82              TestRun.finishCurrentTestExecution();
83          }
84      }
85  
86      @Nullable
87      private static Object executeClassMethod(@NonNull FakeInvocation inv, @NonNull Object[] params) throws Throwable {
88          FrameworkMethod method = inv.getInvokedInstance();
89          assert method != null;
90          handleMockingOutsideTests(method);
91  
92          TestRun.clearCurrentTestInstance();
93          inv.prepareToProceedFromNonRecursiveMock();
94  
95          return method.invokeExplosively(null, params);
96      }
97  
98      private void prepareToExecuteSetupMethod(@NonNull Object target) {
99          discardTestLevelMockedTypes();
100         prepareForNextTest();
101         shouldPrepareForNextTest = false;
102         createInstancesForTestedFieldsBeforeSetup(target);
103     }
104 
105     private static void handleMockingOutsideTests(@NonNull FrameworkMethod it) {
106         Class<?> testClass = it.getMethod().getDeclaringClass();
107 
108         TestRun.enterNoMockingZone();
109 
110         try {
111             Class<?> currentTestClass = TestRun.getCurrentTestClass();
112 
113             if (currentTestClass != null && testClass.isAssignableFrom(currentTestClass)
114                     && it.getAnnotation(AfterClass.class) != null) {
115                 cleanUpMocksFromPreviousTest();
116             }
117 
118             if (it.getAnnotation(BeforeClass.class) != null) {
119                 updateTestClassState(null, testClass);
120             }
121         } finally {
122             TestRun.exitNoMockingZone();
123         }
124     }
125 
126     private static void handleMockingOutsideTestMethods(@NonNull Object target) {
127         Class<?> testClass = target.getClass();
128 
129         TestRun.enterNoMockingZone();
130 
131         try {
132             updateTestClassState(target, testClass);
133         } finally {
134             TestRun.exitNoMockingZone();
135         }
136     }
137 
138     private static void executeTestMethod(@NonNull FakeInvocation invocation, @NonNull Object testInstance,
139             @Nullable Object... parameters) throws Throwable {
140         SavePoint savePoint = new SavePoint();
141 
142         TestRun.setRunningIndividualTest(testInstance);
143 
144         FrameworkMethod it = invocation.getInvokedInstance();
145         assert it != null;
146         Method testMethod = it.getMethod();
147         Throwable testFailure = null;
148         boolean testFailureExpected = false;
149 
150         try {
151             createInstancesForTestedFieldsFromBaseClasses(testInstance);
152             Object[] annotatedParameters = createInstancesForAnnotatedParameters(testInstance, testMethod, parameters);
153             createInstancesForTestedFields(testInstance);
154 
155             invocation.prepareToProceedFromNonRecursiveMock();
156 
157             Object[] params = annotatedParameters == null ? parameters : annotatedParameters;
158             it.invokeExplosively(testInstance, params);
159         } catch (Throwable thrownByTest) {
160             testFailure = thrownByTest;
161             Class<?> expectedType = testMethod.getAnnotation(Test.class).expected();
162             testFailureExpected = expectedType.isAssignableFrom(thrownByTest.getClass());
163         } finally {
164             concludeTestMethodExecution(savePoint, testFailure, testFailureExpected);
165         }
166     }
167 }