private void executeTestMethod(FrameworkMethod it, Object target, Object... parameters) throws Throwable { SavePoint savePoint = new SavePoint(); TestRun.setSavePointForTestMethod(savePoint); Method testMethod = it.getMethod(); Throwable testFailure = null; boolean testFailureExpected = false; try { Object[] mockParameters = null; /** modified by davey.wu * */ String frameworkMethodName = it.getClass().getName(); /** 不判断整个类名,是尽量避免代码package重构带来的影响 * */ boolean isTest4JFrameworkMethod = frameworkMethodName.startsWith("org.test4j.junit.") && frameworkMethodName.endsWith(".FrameworkMethodWithParameters"); if (!isTest4JFrameworkMethod) { mockParameters = createInstancesForMockParameters(target, testMethod, parameters, savePoint); } /** end modified by davey.wu * */ createInstancesForTestedFields(target); TestRun.setRunningIndividualTest(target); it.invokeExplosively(target, mockParameters == null ? parameters : mockParameters); } catch (Throwable thrownByTest) { testFailure = thrownByTest; Class<? extends Throwable> expectedType = testMethod.getAnnotation(Test.class).expected(); testFailureExpected = expectedType.isAssignableFrom(thrownByTest.getClass()); } finally { concludeTestMethodExecution(savePoint, testFailure, testFailureExpected); } }
Object invokeExplosively(FrameworkMethod it, Object target, Object... params) throws Throwable { Method method = it.getMethod(); Class<?> testClass = target == null ? method.getDeclaringClass() : target.getClass(); handleMockingOutsideTestMethods(it, target, testClass); // In case it isn't a test method, but a before/after method: if (it.getAnnotation(Test.class) == null) { if (shouldPrepareForNextTest && it.getAnnotation(Before.class) != null) { prepareForNextTest(); shouldPrepareForNextTest = false; } TestRun.setRunningIndividualTest(target); TestRun.setSavePointForTestMethod(null); try { return it.invokeExplosively(target, params); } catch (Throwable t) { RecordAndReplayExecution.endCurrentReplayIfAny(); StackTrace.filterStackTrace(t); throw t; } finally { if (it.getAnnotation(After.class) != null) { shouldPrepareForNextTest = true; } } } if (shouldPrepareForNextTest) { prepareForNextTest(); } shouldPrepareForNextTest = true; try { executeTestMethod(it, target, params); return null; // it's a test method, therefore has void return type } catch (Throwable t) { StackTrace.filterStackTrace(t); throw t; } finally { /** modified by davey.wu * */ TestRun.finishCurrentTestExecution(true); // TestRun.finishCurrentTestExecution(false); /** end modified by davey.wu * */ } }
private boolean isMatchingInstance( @Nonnull Object invokedInstance, @Nonnull Expectation expectation) { ExpectedInvocation invocation = expectation.invocation; assert invocation.instance != null; if (isEquivalentInstance(invocation.instance, invokedInstance)) { return true; } if (TestRun.getExecutingTest().isInjectableMock(invokedInstance)) { return false; } if (dynamicMockInstancesToMatch != null) { if (containsReference(dynamicMockInstancesToMatch, invokedInstance)) { return false; } Class<?> invokedClass = invocation.instance.getClass(); for (Object dynamicMock : dynamicMockInstancesToMatch) { if (dynamicMock.getClass() == invokedClass) { return false; } } } return !invocation.matchInstance && expectation.recordPhase != null; }
boolean isEquivalentInstance( @Nonnull Object invocationInstance, @Nonnull Object invokedInstance) { return invocationInstance == invokedInstance || invocationInstance == replacementMap.get(invokedInstance) || invocationInstance == instanceMap.get(invokedInstance) || invokedInstance == instanceMap.get(invocationInstance) || TestRun.getExecutingTest() .isInvokedInstanceEquivalentToCapturedInstance(invocationInstance, invokedInstance); }
private boolean isSameMockedClass(@Nullable Object mock1, @Nullable Object mock2) { if (mock1 == mock2) { return true; } if (mock1 != null && mock2 != null) { Class<?> mockedClass1 = mock1.getClass(); Class<?> mockedClass2 = mock2.getClass(); if (mockedClass1 == mockedClass2 || TestRun.getExecutingTest() .isInvokedInstanceEquivalentToCapturedInstance(mock1, mock2)) { return true; } return TestRun.mockFixture().areCapturedClasses(mockedClass1, mockedClass2); } return false; }
private void handleMockingOutsideTestMethods( FrameworkMethod it, Object target, Class<?> testClass) { TestRun.enterNoMockingZone(); try { if (target == null) { Class<?> currentTestClass = TestRun.getCurrentTestClass(); if (currentTestClass != null && testClass.isAssignableFrom(currentTestClass)) { if (it.getAnnotation(AfterClass.class) != null) { cleanUpMocksFromPreviousTestClass(); } } else if (testClass.isAnnotationPresent(SuiteClasses.class)) { setUpClassLevelMocksAndStubs(testClass); } else if (it.getAnnotation(BeforeClass.class) != null) { updateTestClassState(null, testClass); } } else { updateTestClassState(target, testClass); } } finally { TestRun.exitNoMockingZone(); } }
boolean isToBeMatchedOnInstance(@Nullable Object mock, @Nonnull String mockNameAndDesc) { if (mock == null || mockNameAndDesc.charAt(0) == '<') { return false; } if (dynamicMockInstancesToMatch != null && containsReference(dynamicMockInstancesToMatch, mock)) { return true; } if (mockedTypesToMatchOnInstances != null) { Class<?> mockedClass = GeneratedClasses.getMockedClass(mock); if (containsReference(mockedTypesToMatchOnInstances, mockedClass)) { return true; } } else if (TestRun.getExecutingTest().isInjectableMock(mock)) { return true; } return false; }
@Override boolean isToExecuteRealObjectOverride(@Nonnull Object instance) { return !TestRun.getExecutingTest().isMockedInstance(instance); }
@Override boolean isToExecuteRealImplementation(@Nullable Object instance) { return instance == null || !TestRun.getExecutingTest().isMockedInstance(instance); }
@Override boolean isWithRealImplementation(@Nullable Object instance) { return instance == null || !TestRun.getExecutingTest().isInjectableMock(instance); }
@Override boolean isToExecuteRealImplementation(@Nullable Object instance) { return instance != null && !TestRun.mockFixture().isInstanceOfMockedClass(instance); }
void registerRead() { int testId = TestRun.getTestId(); testIdsToAssignments.put(testId, null); readCount++; }
void registerAssignment() { int testId = TestRun.getTestId(); testIdsToAssignments.put(testId, Boolean.TRUE); writeCount++; }
/** * Discards any mocks set up for the specified classes that are currently in effect, for all test * scopes: the current test method (if any), the current test (which starts with the first * "before" method and continues until the last "after" method), the current test class (which * includes all code from the first "before class" method to the last "after class" method), and * the current test suite. * * <p>Notice that if one of the given real classes has a mock class applied at the level of the * test class, calling this method would negate the application of that mock class. JMockit will * automatically restore classes mocked by a test at the end of its execution, as well as all * classes mocked for the test class as a whole (through a "before class" method or an * {@code @UsingMocksAndStubs} annotation) before the first test in the next test class is * executed. * * @param realClasses one or more real classes from production code, which may have mocked methods */ public static void tearDownMocks(Class<?>... realClasses) { Set<Class<?>> classesToRestore = new HashSet<Class<?>>(); Collections.addAll(classesToRestore, realClasses); TestRun.mockFixture().restoreAndRemoveRedefinedClasses(classesToRestore); }
/** * Discards any mocks currently in effect, for all test scopes: the current test method (if any), * the current test (which starts with the first "before" method and continues until the last * "after" method), the current test class (which includes all code from the first "before class" * method to the last "after class" method), and the current test suite. * * <p>Notice that a call to this method will tear down <em>all</em> mock classes that were applied * through use of the Mockups API that are still in effect, as well as any mock classes or stubs * applied to the current test class through {@code @UsingMocksAndStubs}. In other words, it would * effectively prevent mocks to be set up at the test class and test suite levels. So, use it only * if necessary and if it won't discard mock classes that should remain in effect. Consider using * {@link #tearDownMocks(Class...)} instead, which lets you restrict the set of real classes to be * restored. * * <p>JMockit will automatically restore classes mocked by a test at the end of its execution, as * well as all classes mocked for the test class as a whole (through a "before class" method or an * {@code @UsingMocksAndStubs} annotation) before the first test in the next test class is * executed. * * @see <a * href="http://code.google.com/p/jmockit/source/browse/trunk/main/test/mockit/MockAnnotationsTest.java#450"> * Example</a> */ public static void tearDownMocks() { TestRun.mockFixture().restoreAndRemoveRedefinedClasses(null); TestRun.getMockClasses().getRegularMocks().discardInstances(); }