根据surefire 拉起Junit单元测试类 输出的报错日志 跟踪执行过程:
日志1:
java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at ut.com.suning.common.UnitTestHelper.invokPrivate(UnitTestHelper.java:36) at ut.com.suning.scus.web.controller.OverSeaPlantStoreControllerTest.testImportData(OverSeaPlantStoreControllerTest.java:224) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28) at org.powermock.modules.junit4.rule.PowerMockStatement.evaluate(PowerMockRule.java:63) at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:110) at org.junit.rules.RunRules.evaluate(RunRules.java:18) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:283) at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:173) at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:153) at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:128) at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103) Caused by: com.suning.framework.exception.BaseException: java.lang.IllegalArgumentException: [Assertion failed] - this argument is required; it must not be null at com.suning.scus.web.controller.OverSeaPlantStoreController.importData(OverSeaPlantStoreController.java:239) ... 34 more Caused by: java.lang.IllegalArgumentException: [Assertion failed] - this argument is required; it must not be null at org.springframework.util.Assert.notNull(Assert.java:112) at org.springframework.util.Assert.notNull(Assert.java:123) at com.suning.scus.admin.util.ReadExcelUtils.readTitle(ReadExcelUtils.java:71) at com.suning.scus.web.controller.OverSeaPlantStoreController.importData(OverSeaPlantStoreController.java:221) ... 34 more
它的主pom的依赖和日志3一样,但是这个module中没有使用testng:
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-rule-agent</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>日志2:
java.lang.instrument.IllegalClassFormatException: Error while instrumenting class com/suning/epp/sws/fundrecharge/esb/impl/FundResultCallbackBizImpl$1. at org.jacoco.agent.rt.internal_6da5971.CoverageTransformer.transform(CoverageTransformer.java:93) at sun.instrument.TransformerManager.transform(TransformerManager.java:169) at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:365) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631) at java.lang.ClassLoader.defineClass(ClassLoader.java:615) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141) at java.net.URLClassLoader.defineClass(URLClassLoader.java:283) at java.net.URLClassLoader.access$000(URLClassLoader.java:58) at java.net.URLClassLoader$1.run(URLClassLoader.java:197) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:190) at java.lang.ClassLoader.loadClass(ClassLoader.java:306) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:247) at com.suning.epp.sws.fundrecharge.esb.impl.FundResultCallbackBizImpl.callback(FundResultCallbackBizImpl.java:47) at com.suning.epp.sws.fundwithdraw.FundWithdrawResultListenersTest.testFundHandleTest(FundWithdrawResultListenersTest.java:66) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:367) at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:274) at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:238) at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:161) at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:290) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:242) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:121) Caused by: java.io.IOException: Error while instrumenting class com/suning/epp/sws/fundrecharge/esb/impl/FundResultCallbackBizImpl$1. at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrumentError(Instrumenter.java:160) at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrument(Instrumenter.java:111) at org.jacoco.agent.rt.internal_6da5971.CoverageTransformer.transform(CoverageTransformer.java:91) ... 45 more Caused by: java.lang.IllegalStateException: Class com/suning/epp/sws/fundrecharge/esb/impl/FundResultCallbackBizImpl$1 is already instrumented. at org.jacoco.agent.rt.internal_6da5971.core.internal.instr.InstrSupport.assertNotInstrumented(InstrSupport.java:89) at org.jacoco.agent.rt.internal_6da5971.core.internal.instr.ClassInstrumenter.visitField(ClassInstrumenter.java:55) at org.jacoco.agent.rt.internal_6da5971.asm.ClassVisitor.visitField(ClassVisitor.java:272) at org.jacoco.agent.rt.internal_6da5971.asm.ClassReader.readField(ClassReader.java:768) at org.jacoco.agent.rt.internal_6da5971.asm.ClassReader.accept(ClassReader.java:689) at org.jacoco.agent.rt.internal_6da5971.asm.ClassReader.accept(ClassReader.java:506) at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrument(Instrumenter.java:84) at org.jacoco.agent.rt.internal_6da5971.core.instr.Instrumenter.instrument(Instrumenter.java:108) ... 46 more
日志3:testng和junit混合使用,junit编写测试类,testng xml声明测试参数,但是testng是在主pom中声明的:
java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at ut.com.suning.common.UnitTestHelper.invokPrivate(UnitTestHelper.java:36) at ut.com.suning.scus.common.rule.PublicRuleUtilsTest.testTransToMdmCityCode(PublicRuleUtilsTest.java:632) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28) at org.powermock.modules.junit4.rule.PowerMockStatement.evaluate(PowerMockRule.java:63) at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:110) at org.junit.rules.RunRules.evaluate(RunRules.java:18) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at org.junit.runner.JUnitCore.run(JUnitCore.java:136) at org.testng.junit.JUnit4TestRunner.start(JUnit4TestRunner.java:81) at org.testng.junit.JUnit4TestRunner.run(JUnit4TestRunner.java:69) at org.testng.TestRunner$1.run(TestRunner.java:682) at org.testng.TestRunner.runWorkers(TestRunner.java:1005) at org.testng.TestRunner.privateRunJUnit(TestRunner.java:713) at org.testng.TestRunner.run(TestRunner.java:614) at org.testng.SuiteRunner.runTest(SuiteRunner.java:348) at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:343) at org.testng.SuiteRunner.privateRun(SuiteRunner.java:305) at org.testng.SuiteRunner.run(SuiteRunner.java:254) at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52) at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86) at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224) at org.testng.TestNG.runSuitesLocally(TestNG.java:1149) at org.testng.TestNG.run(TestNG.java:1057) at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:115) at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.executeMulti(TestNGDirectoryTestSuite.java:212) at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:108) at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:111) at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103) Caused by: java.lang.NullPointerException at com.suning.scus.common.rule.PublicRuleUtils.transToMdmCityCode(PublicRuleUtils.java:161) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.powermock.api.mockito.internal.invocation.MockitoMethodInvocationControl$1.invoke(MockitoMethodInvocationControl.java:242) at org.powermock.api.mockito.internal.invocation.MockitoMethodInvocationControl.performIntercept(MockitoMethodInvocationControl.java:260) at org.powermock.api.mockito.internal.invocation.MockitoMethodInvocationControl.invoke(MockitoMethodInvocationControl.java:192) at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:124) at org.powermock.core.MockGateway.methodCall(MockGateway.java:63) at com.suning.scus.common.rule.PublicRuleUtils.transToMdmCityCode(PublicRuleUtils.java) ... 51 more
pom文件声明依赖有testng,junit,springtest,powermock-junit-rule,等
之所以混用,是因为需要使用testng声明xml,在xml中添加参数:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Suite1" verbose="1">
<test name="appMoudle1">
<classes>
</classes>
</test>
<!-- <test name="appMoudle2">
<parameter name="dataSetFile" value="/dataset/dataset-module2.xml" />
<classes>
<class name="com.framework.test.DbInitializer" />
<class name="com.sample.UserTest" />
</classes>
</test> -->
</suite> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-rule-agent</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-version}</version>
<scope>test</scope>
</dependency>根据日志1进行源码阅读跟踪:
ForkedBooter代码:
public final class ForkedBooter
{
private static final long SYSTEM_EXIT_TIMEOUT_IN_SECONDS = 30;
private static final long PING_TIMEOUT_IN_SECONDS = 20;
private static final ScheduledExecutorService JVM_TERMINATOR = createJvmTerminator();
/**
* This method is invoked when Surefire is forked - this method parses and organizes the arguments passed to it and
* then calls the Surefire class' run method. <p/> The system exit code will be 1 if an exception is thrown.
*
* @param args Commandline arguments
*/
public static void main( String... args )
{
final CommandReader reader = startupMasterProcessReader();
final ScheduledFuture<?> pingScheduler = listenToShutdownCommands( reader );
final PrintStream originalOut = System.out;
try
{
if ( args.length > 1 )
{
SystemPropertyManager.setSystemProperties( new File( args[1] ) );
}
File surefirePropertiesFile = new File( args[0] );
InputStream stream = surefirePropertiesFile.exists() ? new FileInputStream( surefirePropertiesFile ) : null;
BooterDeserializer booterDeserializer = new BooterDeserializer( stream );
ProviderConfiguration providerConfiguration = booterDeserializer.deserialize();
final StartupConfiguration startupConfiguration = booterDeserializer.getProviderConfiguration();
TypeEncodedValue forkedTestSet = providerConfiguration.getTestForFork();
boolean readTestsFromInputStream = providerConfiguration.isReadTestsFromInStream();
final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
if ( startupConfiguration.isManifestOnlyJarRequestedAndUsable() )
{
classpathConfiguration.trickClassPathWhenManifestOnlyClasspath();
}
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
classLoader.setDefaultAssertionStatus( classpathConfiguration.isEnableAssertions() );
startupConfiguration.writeSurefireTestClasspathProperty();
final Object testSet;
if ( forkedTestSet != null )
{
testSet = forkedTestSet.getDecodedValue( classLoader );
}
else if ( readTestsFromInputStream )
{
testSet = new LazyTestsToRun( originalOut );
}
else
{
testSet = null;
}
try
{
runSuitesInProcess( testSet, startupConfiguration, providerConfiguration, originalOut );
}
catch ( InvocationTargetException t )
{
LegacyPojoStackTraceWriter stackTraceWriter =
new LegacyPojoStackTraceWriter( "test subystem", "no method", t.getTargetException() );
StringBuilder stringBuilder = new StringBuilder();
encode( stringBuilder, stackTraceWriter, false );
encodeAndWriteToOutput( ( (char) BOOTERCODE_ERROR ) + ",0," + stringBuilder + "\n" , originalOut );
}
catch ( Throwable t )
{
StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( "test subystem", "no method", t );
StringBuilder stringBuilder = new StringBuilder();
encode( stringBuilder, stackTraceWriter, false );
encodeAndWriteToOutput( ( (char) BOOTERCODE_ERROR ) + ",0," + stringBuilder + "\n", originalOut );
}
// Say bye.
encodeAndWriteToOutput( ( (char) BOOTERCODE_BYE ) + ",0,BYE!\n", originalOut );
originalOut.flush();
// noinspection CallToSystemExit
exit( 0, EXIT, reader, false );
}
catch ( Throwable t )
{
// Just throwing does getMessage() and a local trace - we want to call printStackTrace for a full trace
// noinspection UseOfSystemOutOrSystemErr
t.printStackTrace( System.err );
// noinspection ProhibitedExceptionThrown,CallToSystemExit
exit( 1, EXIT, reader, false );
}
finally
{
pingScheduler.cancel( true );
}
}
private static CommandReader startupMasterProcessReader()
{
return CommandReader.getReader();
}
private static ScheduledFuture<?> listenToShutdownCommands( CommandReader reader )
{
reader.addShutdownListener( createExitHandler( reader ) );
AtomicBoolean pingDone = new AtomicBoolean( true );
reader.addNoopListener( createPingHandler( pingDone ) );
return JVM_TERMINATOR.scheduleAtFixedRate( createPingJob( pingDone, reader ),
0, PING_TIMEOUT_IN_SECONDS, SECONDS );
}
private static CommandListener createPingHandler( final AtomicBoolean pingDone )
{
return new CommandListener()
{
public void update( Command command )
{
pingDone.set( true );
}
};
}
private static CommandListener createExitHandler( final CommandReader reader )
{
return new CommandListener()
{
public void update( Command command )
{
exit( 1, command.toShutdownData(), reader, true );
}
};
}
private static Runnable createPingJob( final AtomicBoolean pingDone, final CommandReader reader )
{
return new Runnable()
{
public void run()
{
boolean hasPing = pingDone.getAndSet( false );
if ( !hasPing )
{
exit( 1, KILL, reader, true );
}
}
};
}
private static void encodeAndWriteToOutput( String string, PrintStream out )
{
byte[] encodeBytes = encodeStringForForkCommunication( string );
out.write( encodeBytes, 0, encodeBytes.length );
}
private static void exit( int returnCode, Shutdown shutdownType, CommandReader reader, boolean stopReaderOnExit )
{
switch ( shutdownType )
{
case KILL:
Runtime.getRuntime().halt( returnCode );
case EXIT:
if ( stopReaderOnExit )
{
reader.stop();
}
launchLastDitchDaemonShutdownThread( returnCode );
System.exit( returnCode );
case DEFAULT:
// refers to shutdown=testset, but not used now, keeping reader open
default:
break;
}
}
private static RunResult runSuitesInProcess( Object testSet, StartupConfiguration startupConfiguration,
ProviderConfiguration providerConfiguration,
PrintStream originalSystemOut )
throws SurefireExecutionException, TestSetFailedException, InvocationTargetException
{
final ReporterFactory factory = createForkingReporterFactory( providerConfiguration, originalSystemOut );
return invokeProviderInSameClassLoader( testSet, factory, providerConfiguration, true, startupConfiguration,
false );
}
private static ReporterFactory createForkingReporterFactory( ProviderConfiguration providerConfiguration,
PrintStream originalSystemOut )
{
final boolean trimStackTrace = providerConfiguration.getReporterConfiguration().isTrimStackTrace();
return SurefireReflector.createForkingReporterFactoryInCurrentClassLoader( trimStackTrace, originalSystemOut );
}
private static ScheduledExecutorService createJvmTerminator()
{
ThreadFactory threadFactory = newDaemonThreadFactory( "last-ditch-daemon-shutdown-thread-"
+ SYSTEM_EXIT_TIMEOUT_IN_SECONDS
+ "sec" );
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor( 1, threadFactory );
executor.setMaximumPoolSize( 1 );
executor.prestartCoreThread();
return executor;
}
@SuppressWarnings( "checkstyle:emptyblock" )
private static void launchLastDitchDaemonShutdownThread( final int returnCode )
{
JVM_TERMINATOR.schedule( new Runnable()
{
public void run()
{
Runtime.getRuntime().halt( returnCode );
}
}, SYSTEM_EXIT_TIMEOUT_IN_SECONDS, SECONDS );
}
private static RunResult invokeProviderInSameClassLoader( Object testSet, Object factory,
ProviderConfiguration providerConfiguration,
boolean insideFork,
StartupConfiguration startupConfig,
boolean restoreStreams )
throws TestSetFailedException, InvocationTargetException
{
final PrintStream orgSystemOut = System.out;
final PrintStream orgSystemErr = System.err;
// Note that System.out/System.err are also read in the "ReporterConfiguration" instatiation
// in createProvider below. These are the same values as here.
try
{
return createProviderInCurrentClassloader( startupConfig, insideFork, providerConfiguration, factory )
.invoke( testSet );
}
finally
{
if ( restoreStreams && System.getSecurityManager() == null )
{
System.setOut( orgSystemOut );
System.setErr( orgSystemErr );
}
}
}
private static SurefireProvider createProviderInCurrentClassloader( StartupConfiguration startupConfiguration1,
boolean isInsideFork,
ProviderConfiguration providerConfiguration,
Object reporterManagerFactory1 )
{
BaseProviderFactory bpf = new BaseProviderFactory( (ReporterFactory) reporterManagerFactory1, isInsideFork );
bpf.setTestRequest( providerConfiguration.getTestSuiteDefinition() );
bpf.setReporterConfiguration( providerConfiguration.getReporterConfiguration() );
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
bpf.setClassLoaders( classLoader );
bpf.setTestArtifactInfo( providerConfiguration.getTestArtifact() );
bpf.setProviderProperties( providerConfiguration.getProviderProperties() );
bpf.setRunOrderParameters( providerConfiguration.getRunOrderParameters() );
bpf.setDirectoryScannerParameters( providerConfiguration.getDirScannerParams() );
bpf.setMainCliOptions( providerConfiguration.getMainCliOptions() );
bpf.setSkipAfterFailureCount( providerConfiguration.getSkipAfterFailureCount() );
bpf.setShutdown( providerConfiguration.getShutdown() );
String providerClass = startupConfiguration1.getActualClassName();
return (SurefireProvider) ReflectionUtils.instantiateOneArg( classLoader, providerClass, ProviderParameters.class, bpf );
}
}ReflectionUtils代码:
public final class ReflectionUtils
{
private static final Class[] NO_ARGS = new Class[0];
private static final Object[] NO_ARGS_VALUES = new Object[0];
private ReflectionUtils()
{
throw new IllegalStateException("no instantiable constructor");
}
public static Method getMethod(Object instance, String methodName, Class<?>... parameters)
{
return getMethod(instance.getClass(), methodName, parameters);
}
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameters)
{
try
{
return clazz.getMethod(methodName, parameters);
}
catch (NoSuchMethodException e)
{
throw new RuntimeException("When finding method " + methodName, e);
}
}
public static Method tryGetMethod(Class<?> clazz, String methodName, Class<?>... parameters)
{
try
{
return clazz.getMethod(methodName, parameters);
}
catch (NoSuchMethodException e) {}
return null;
}
public static Object invokeGetter(Object instance, String methodName)
{
Method method = getMethod(instance, methodName, NO_ARGS);
return invokeMethodWithArray(instance, method, NO_ARGS_VALUES);
}
public static Constructor getConstructor(Class<?> clazz, Class<?>... arguments)
{
try
{
return clazz.getConstructor(arguments);
}
catch (NoSuchMethodException e)
{
throw new SurefireReflectionException(e);
}
}
public static Object newInstance(Constructor constructor, Object... params)
{
try
{
return constructor.newInstance(params);
}
catch (InvocationTargetException e)
{
throw new SurefireReflectionException(e);
}
catch (InstantiationException e)
{
throw new SurefireReflectionException(e);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
}
public static <T> T instantiate(ClassLoader classLoader, String classname, Class<T> returnType)
{
try
{
Class<?> clazz = loadClass(classLoader, classname);
return returnType.cast(clazz.newInstance());
}
catch (InstantiationException e)
{
throw new SurefireReflectionException(e);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
}
public static Object instantiateOneArg(ClassLoader classLoader, String className, Class<?> param1Class, Object param1)
{
try
{
Class<?> aClass = loadClass(classLoader, className);
Constructor constructor = getConstructor(aClass, new Class[] { param1Class });
return constructor.newInstance(new Object[] { param1 });
}
catch (InvocationTargetException e)
{
throw new SurefireReflectionException(e.getTargetException());
}
catch (InstantiationException e)
{
throw new SurefireReflectionException(e);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
}
public static Object instantiateTwoArgs(ClassLoader classLoader, String className, Class<?> param1Class, Object param1, Class param2Class, Object param2)
{
try
{
Class<?> aClass = loadClass(classLoader, className);
Constructor constructor = getConstructor(aClass, new Class[] { param1Class, param2Class });
return constructor.newInstance(new Object[] { param1, param2 });
}
catch (InvocationTargetException e)
{
throw new SurefireReflectionException(e.getTargetException());
}
catch (InstantiationException e)
{
throw new SurefireReflectionException(e);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
}
public static void invokeSetter(Object o, String name, Class<?> value1clazz, Object value)
{
Method setter = getMethod(o, name, new Class[] { value1clazz });
invokeSetter(o, setter, value);
}
public static Object invokeSetter(Object target, Method method, Object value)
{
return invokeMethodWithArray(target, method, new Object[] { value });
}
public static Object invokeMethodWithArray(Object target, Method method, Object... args)
{
try
{
return method.invoke(target, args);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
catch (InvocationTargetException e)
{
throw new SurefireReflectionException(e.getTargetException());
}
}
public static Object invokeMethodWithArray2(Object target, Method method, Object... args)
throws InvocationTargetException
{
try
{
return method.invoke(target, args);
}
catch (IllegalAccessException e)
{
throw new SurefireReflectionException(e);
}
}
public static Object instantiateObject(String className, Class[] types, Object[] params, ClassLoader classLoader)
{
Class<?> clazz = loadClass(classLoader, className);
Constructor constructor = getConstructor(clazz, types);
return newInstance(constructor, params);
}
public static Class<?> tryLoadClass(ClassLoader classLoader, String className)
{
try
{
return classLoader.loadClass(className);
}
catch (NoClassDefFoundError localNoClassDefFoundError) {}catch (ClassNotFoundException localClassNotFoundException) {}
return null;
}
public static Class<?> loadClass(ClassLoader classLoader, String className)
{
try
{
return classLoader.loadClass(className);
}
catch (NoClassDefFoundError e)
{
throw new SurefireReflectionException(e);
}
catch (ClassNotFoundException e)
{
throw new SurefireReflectionException(e);
}
}
}junit和surefire中间调度很重要的代码是SurefireProvider
public abstract interface SurefireProvider
{
public abstract Iterable<Class<?>> getSuites();
public abstract RunResult invoke(Object paramObject)
throws TestSetFailedException, ReporterException, InvocationTargetException;
public abstract void cancel();
}
surefire-api 中有一个abstractProvider,起到中间连接的作用:
public abstract class AbstractProvider
implements SurefireProvider
{
private final Thread creatingThread = Thread.currentThread();
public void cancel()
{
synchronized (this.creatingThread)
{
if (this.creatingThread.isAlive()) {
this.creatingThread.interrupt();
}
}
}
}
surefire-junit4.jar中有两个class,一个是JUnit4Provider,一个是TestResolverFilter
public JUnit4Provider( ProviderParameters bootParams )
{
// don't start a thread in CommandReader while we are in in-plugin process
commandsReader = bootParams.isInsideFork() ? getReader().setShutdown( bootParams.getShutdown() ) : null;
providerParameters = bootParams;
testClassLoader = bootParams.getTestClassLoader();
scanResult = bootParams.getScanResult();
runOrderCalculator = bootParams.getRunOrderCalculator();
String listeners = bootParams.getProviderProperties().get( "listener" );
customRunListeners = unmodifiableCollection( createCustomListeners( listeners ) );
jUnit4TestChecker = new JUnit4TestChecker( testClassLoader );
TestRequest testRequest = bootParams.getTestRequest();
testResolver = testRequest.getTestListResolver();
rerunFailingTestsCount = testRequest.getRerunFailingTestsCount();
}
public RunResult invoke( Object forkTestSet )
throws TestSetFailedException
{
upgradeCheck();
ReporterFactory reporterFactory = providerParameters.getReporterFactory();
RunResult runResult;
try
{
RunListener reporter = reporterFactory.createReporter();
startCapture( (ConsoleOutputReceiver) reporter );
// startCapture() called in prior to setTestsToRun()
if ( testsToRun == null )
{
setTestsToRun( forkTestSet );
}
Notifier notifier = new Notifier( new JUnit4RunListener( reporter ), getSkipAfterFailureCount() );
Result result = new Result();
notifier.addListeners( customRunListeners )
.addListener( result.createListener() );
if ( isFailFast() && commandsReader != null )
{
registerPleaseStopJUnitListener( notifier );
}
try
{
notifier.fireTestRunStarted( testsToRun.allowEagerReading()
? createTestsDescription( testsToRun )
: createDescription( UNDETERMINED_TESTS_DESCRIPTION ) );
if ( commandsReader != null )
{
registerShutdownListener( testsToRun );
commandsReader.awaitStarted();
}
for ( Class<?> testToRun : testsToRun )
{
executeTestSet( testToRun, reporter, notifier );
}
}
finally
{
notifier.fireTestRunFinished( result );
notifier.removeListeners();
}
rethrowAnyTestMechanismFailures( result );
}
finally
{
runResult = reporterFactory.close();
}
return runResult;
}
private void setTestsToRun( Object forkTestSet )
throws TestSetFailedException
{
if ( forkTestSet instanceof TestsToRun )
{
testsToRun = (TestsToRun) forkTestSet;
}
else if ( forkTestSet instanceof Class )
{
testsToRun = fromClass( (Class<?>) forkTestSet );
}
else
{
testsToRun = scanClassPath();
}
}
private boolean isRerunFailingTests()
{
return rerunFailingTestsCount > 0;
}
private boolean isFailFast()
{
return providerParameters.getSkipAfterFailureCount() > 0;
}
private int getSkipAfterFailureCount()
{
return isFailFast() ? providerParameters.getSkipAfterFailureCount() : 0;
}
private void registerShutdownListener( final TestsToRun testsToRun )
{
commandsReader.addShutdownListener( new CommandListener()
{
public void update( Command command )
{
testsToRun.markTestSetFinished();
}
} );
}
private void registerPleaseStopJUnitListener( final Notifier notifier )
{
commandsReader.addSkipNextTestsListener( new CommandListener()
{
public void update( Command command )
{
notifier.pleaseStop();
}
} );
}
private void executeTestSet( Class<?> clazz, RunListener reporter, Notifier notifier )
{
final ReportEntry report = new SimpleReportEntry( getClass().getName(), clazz.getName() );
reporter.testSetStarting( report );
try
{
executeWithRerun( clazz, notifier );
}
catch ( Throwable e )
{
if ( isFailFast() && e instanceof StoppedByUserException )
{
String reason = e.getClass().getName();
Description skippedTest = createDescription( clazz.getName(), createIgnored( reason ) );
notifier.fireTestIgnored( skippedTest );
}
else
{
String reportName = report.getName();
String reportSourceName = report.getSourceName();
PojoStackTraceWriter stackWriter = new PojoStackTraceWriter( reportSourceName, reportName, e );
reporter.testError( withException( reportSourceName, reportName, stackWriter ) );
}
}
finally
{
reporter.testSetCompleted( report );
}
}
private void executeWithRerun( Class<?> clazz, Notifier notifier )
throws TestSetFailedException
{
JUnitTestFailureListener failureListener = new JUnitTestFailureListener();
notifier.addListener( failureListener );
boolean hasMethodFilter = testResolver != null && testResolver.hasMethodPatterns();
try
{
try
{
notifier.asFailFast( isFailFast() );
execute( clazz, notifier, hasMethodFilter ? createMethodFilter() : null );
}
finally
{
notifier.asFailFast( false );
}
// Rerun failing tests if rerunFailingTestsCount is larger than 0
if ( isRerunFailingTests() )
{
Notifier rerunNotifier = pureNotifier();
notifier.copyListenersTo( rerunNotifier );
for ( int i = 0; i < rerunFailingTestsCount && !failureListener.getAllFailures().isEmpty(); i++ )
{
Set<ClassMethod> failedTests = generateFailingTests( failureListener.getAllFailures() );
failureListener.reset();
if ( !failedTests.isEmpty() )
{
executeFailedMethod( rerunNotifier, failedTests );
}
}
}
}
finally
{
notifier.removeListener( failureListener );
}
}
public Iterable<Class<?>> getSuites()
{
testsToRun = scanClassPath();
return testsToRun;
}
private TestsToRun scanClassPath()
{
final TestsToRun scannedClasses = scanResult.applyFilter( jUnit4TestChecker, testClassLoader );
return runOrderCalculator.orderTestClasses( scannedClasses );
}
private void upgradeCheck()
throws TestSetFailedException
{
if ( isJUnit4UpgradeCheck() )
{
Collection<Class<?>> classesSkippedByValidation =
scanResult.getClassesSkippedByValidation( jUnit4TestChecker, testClassLoader );
if ( !classesSkippedByValidation.isEmpty() )
{
StringBuilder reason = new StringBuilder();
reason.append( "Updated check failed\n" );
reason.append( "There are tests that would be run with junit4 / surefire 2.6 but not with [2.7,):\n" );
for ( Class testClass : classesSkippedByValidation )
{
reason.append( " " );
reason.append( testClass.getName() );
reason.append( "\n" );
}
throw new TestSetFailedException( reason.toString() );
}
}
}
static Description createTestsDescription( Iterable<Class<?>> classes )
{
// "null" string rather than null; otherwise NPE in junit:4.0
Description description = createDescription( "null" );
for ( Class<?> clazz : classes )
{
description.addChild( createDescription( clazz.getName() ) );
}
return description;
}
private static boolean isJUnit4UpgradeCheck()
{
return System.getProperty( "surefire.junit4.upgradecheck" ) != null;
}
private static void execute( Class<?> testClass, Notifier notifier, Filter filter )
{
final int classModifiers = testClass.getModifiers();
if ( !isAbstract( classModifiers ) && !isInterface( classModifiers ) )
{
Request request = aClass( testClass );
if ( filter != null )
{
request = request.filterWith( filter );
}
Runner runner = request.getRunner();
if ( countTestsInRunner( runner.getDescription() ) != 0 )
{
runner.run( notifier );
}
}
}
private void executeFailedMethod( RunNotifier notifier, Set<ClassMethod> failedMethods )
throws TestSetFailedException
{
for ( ClassMethod failedMethod : failedMethods )
{
try
{
Class<?> methodClass = Class.forName( failedMethod.getClazz(), true, testClassLoader );
String methodName = failedMethod.getMethod();
method( methodClass, methodName ).getRunner().run( notifier );
}
catch ( ClassNotFoundException e )
{
throw new TestSetFailedException( "Unable to create test class '" + failedMethod.getClazz() + "'", e );
}
}
}
/**
* JUnit error: test count includes one test-class as a suite which has filtered out all children.
* Then the child test has a description "initializationError0(org.junit.runner.manipulation.Filter)"
* for JUnit 4.0 or "initializationError(org.junit.runner.manipulation.Filter)" for JUnit 4.12
* and Description#isTest() returns true, but this description is not a real test
* and therefore it should not be included in the entire test count.
*/
private static int countTestsInRunner( Description description )
{
if ( description.isSuite() )
{
int count = 0;
for ( Description child : description.getChildren() )
{
if ( !hasFilteredOutAllChildren( child ) )
{
count += countTestsInRunner( child );
}
}
return count;
}
else if ( description.isTest() )
{
return hasFilteredOutAllChildren( description ) ? 0 : 1;
}
else
{
return 0;
}
}
private static boolean hasFilteredOutAllChildren( Description description )
{
String name = description.getDisplayName();
// JUnit 4.0: initializationError0; JUnit 4.12: initializationError.
if ( name == null )
{
return true;
}
else
{
name = name.trim();
return name.startsWith( "initializationError0(org.junit.runner.manipulation.Filter)" )
|| name.startsWith( "initializationError(org.junit.runner.manipulation.Filter)" );
}
}
private Filter createMethodFilter()
{
TestListResolver methodFilter = optionallyWildcardFilter( testResolver );
return methodFilter.isEmpty() || methodFilter.isWildcard() ? null : new TestResolverFilter( methodFilter );
}
}上述的类中execute方法直接调用的junit中的类Runner:
private static void execute( Class<?> testClass, Notifier notifier, Filter filter )
{
final int classModifiers = testClass.getModifiers();
if ( !isAbstract( classModifiers ) && !isInterface( classModifiers ) )
{
Request request = aClass( testClass );
if ( filter != null )
{
request = request.filterWith( filter );
}
Runner runner = request.getRunner();
if ( countTestsInRunner( runner.getDescription() ) != 0 )
{
runner.run( notifier );
}
}
}junit4中Runner仅仅是一个抽象的类:
public abstract class Runner implements Describable {
/* (non-Javadoc)
* @see org.junit.runner.Describable#getDescription()
*/
public abstract Description getDescription();
/**
* Run the tests for this runner.
* @param notifier will be notified of events while tests are being run--tests being
* started, finishing, and failing
*/
public abstract void run(RunNotifier notifier);
/**
* @return the number of tests to be run by the receiver
*/
public int testCount() {
return getDescription().testCount();
}
}
Describable:
abstract Describable{ /**
* @return a {@link Description} showing the tests to be run by the receiver
*/
public abstract Description getDescription();
}然后是org.junit.runners这个包里面ParentRunner类,这个类极为关键:
public abstract class ParentRunner<T> extends Runner implements Filterable,
Sortable {
private final TestClass fTestClass;
private Filter fFilter= null;
private Sorter fSorter= Sorter.NULL;
/**
* Constructs a new {@code ParentRunner} that will run {@code @TestClass}
* @throws InitializationError
*/
protected ParentRunner(Class<?> testClass) throws InitializationError {
fTestClass= new TestClass(testClass);
validate();
}
//
// Must be overridden
//
/**
* Returns a list of objects that define the children of this Runner.
*/
protected abstract List<T> getChildren();
/**
* Returns a {@link Description} for {@code child}, which can be assumed to
* be an element of the list returned by {@link ParentRunner#getChildren()}
*/
protected abstract Description describeChild(T child);
/**
* Runs the test corresponding to {@code child}, which can be assumed to be
* an element of the list returned by {@link ParentRunner#getChildren()}.
* Subclasses are responsible for making sure that relevant test events are
* reported through {@code notifier}
*/
protected abstract void runChild(T child, RunNotifier notifier);
//
// May be overridden
//
/**
* Adds to {@code errors} a throwable for each problem noted with the test class (available from {@link #getTestClass()}).
* Default implementation adds an error for each method annotated with
* {@code @BeforeClass} or {@code @AfterClass} that is not
* {@code public static void} with no arguments.
*/
protected void collectInitializationErrors(List<Throwable> errors) {
validatePublicVoidNoArgMethods(BeforeClass.class, true, errors);
validatePublicVoidNoArgMethods(AfterClass.class, true, errors);
}
/**
* Adds to {@code errors} if any method in this class is annotated with
* {@code annotation}, but:
* <ul>
* <li>is not public, or
* <li>takes parameters, or
* <li>returns something other than void, or
* <li>is static (given {@code isStatic is false}), or
* <li>is not static (given {@code isStatic is true}).
*/
protected void validatePublicVoidNoArgMethods(Class<? extends Annotation> annotation,
boolean isStatic, List<Throwable> errors) {
List<FrameworkMethod> methods= getTestClass().getAnnotatedMethods(annotation);
for (FrameworkMethod eachTestMethod : methods)
eachTestMethod.validatePublicVoidNoArg(isStatic, errors);
}
/**
* Constructs a {@code Statement} to run all of the tests in the test class. Override to add pre-/post-processing.
* Here is an outline of the implementation:
* <ul>
* <li>Call {@link #runChild(Object, RunNotifier)} on each object returned by {@link #getChildren()} (subject to any imposed filter and sort).</li>
* <li>ALWAYS run all non-overridden {@code @BeforeClass} methods on this class
* and superclasses before the previous step; if any throws an
* Exception, stop execution and pass the exception on.
* <li>ALWAYS run all non-overridden {@code @AfterClass} methods on this class
* and superclasses before any of the previous steps; all AfterClass methods are
* always executed: exceptions thrown by previous steps are combined, if
* necessary, with exceptions from AfterClass methods into a
* {@link MultipleFailureException}.
* </ul>
* @param notifier
* @return {@code Statement}
*/
protected Statement classBlock(final RunNotifier notifier) {
Statement statement= childrenInvoker(notifier);
statement= withBeforeClasses(statement);
statement= withAfterClasses(statement);
return statement;
}
/**
* Returns a {@link Statement}: run all non-overridden {@code @BeforeClass} methods on this class
* and superclasses before executing {@code statement}; if any throws an
* Exception, stop execution and pass the exception on.
*/
protected Statement withBeforeClasses(Statement statement) {
List<FrameworkMethod> befores= fTestClass
.getAnnotatedMethods(BeforeClass.class);
statement= new RunBefores(statement, befores, null);
return statement;
}
/**
* Returns a {@link Statement}: run all non-overridden {@code @AfterClass} methods on this class
* and superclasses before executing {@code statement}; all AfterClass methods are
* always executed: exceptions thrown by previous steps are combined, if
* necessary, with exceptions from AfterClass methods into a
* {@link MultipleFailureException}.
*/
protected Statement withAfterClasses(Statement statement) {
List<FrameworkMethod> afters= fTestClass
.getAnnotatedMethods(AfterClass.class);
statement= new RunAfters(statement, afters, null);
return statement;
}
/**
* Returns a {@link Statement}: Call {@link #runChild(Object, RunNotifier)}
* on each object returned by {@link #getChildren()} (subject to any imposed
* filter and sort)
*/
protected Statement childrenInvoker(final RunNotifier notifier) {
return new Statement() {
@Override
public void evaluate() {
runChildren(notifier);
}
};
}
private void runChildren(final RunNotifier notifier) {
for (T each : getFilteredChildren())
runChild(each, notifier);
}
/**
* Returns a name used to describe this Runner
*/
protected String getName() {
return fTestClass.getName();
}
//
// Available for subclasses
//
/**
* Returns a {@link TestClass} object wrapping the class to be executed.
*/
protected final TestClass getTestClass() {
return fTestClass;
}
//
// Implementation of Runner
//
@Override
public Description getDescription() {
Description description= Description.createSuiteDescription(getName(),
fTestClass.getAnnotations());
for (T child : getFilteredChildren())
description.addChild(describeChild(child));
return description;
}
@Override
public void run(final RunNotifier notifier) {
EachTestNotifier testNotifier= new EachTestNotifier(notifier,
getDescription());
try {
Statement statement= classBlock(notifier);
statement.evaluate();
} catch (AssumptionViolatedException e) {
testNotifier.fireTestIgnored();
} catch (StoppedByUserException e) {
throw e;
} catch (Throwable e) {
testNotifier.addFailure(e);
}
}
//
// Implementation of Filterable and Sortable
//
public void filter(Filter filter) throws NoTestsRemainException {
fFilter= filter;
for (T each : getChildren())
if (shouldRun(each))
return;
throw new NoTestsRemainException();
}
public void sort(Sorter sorter) {
fSorter= sorter;
}
//
// Private implementation
//
private void validate() throws InitializationError {
List<Throwable> errors= new ArrayList<Throwable>();
collectInitializationErrors(errors);
if (!errors.isEmpty())
throw new InitializationError(errors);
}
protected List<T> getFilteredChildren() {
ArrayList<T> filtered= new ArrayList<T>();
for (T each : getChildren())
if (shouldRun(each))
try {
filterChild(each);
sortChild(each);
filtered.add(each);
} catch (NoTestsRemainException e) {
// don't add it
}
Collections.sort(filtered, comparator());
return filtered;
}
private void sortChild(T child) {
fSorter.apply(child);
}
private void filterChild(T child) throws NoTestsRemainException {
if (fFilter != null)
fFilter.apply(child);
}
private boolean shouldRun(T each) {
return fFilter == null || fFilter.shouldRun(describeChild(each));
}
private Comparator<? super T> comparator() {
return new Comparator<T>() {
public int compare(T o1, T o2) {
return fSorter.compare(describeChild(o1), describeChild(o2));
}
};
}
}然后是BlockJUnit4ClassRunner 继承了ParentRunner:
public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod> {
/**
* Creates a BlockJUnit4ClassRunner to run {@code klass}
*
* @throws InitializationError
* if the test class is malformed.
*/
public BlockJUnit4ClassRunner(Class<?> klass) throws InitializationError {
super(klass);
}
//
// Implementation of ParentRunner
//
@Override
protected void runChild(FrameworkMethod method, RunNotifier notifier) {
EachTestNotifier eachNotifier= makeNotifier(method, notifier);
if (method.getAnnotation(Ignore.class) != null) {
eachNotifier.fireTestIgnored();
return;
}
eachNotifier.fireTestStarted();
try {
methodBlock(method).evaluate();
} catch (AssumptionViolatedException e) {
eachNotifier.addFailedAssumption(e);
} catch (Throwable e) {
eachNotifier.addFailure(e);
} finally {
eachNotifier.fireTestFinished();
}
}
@Override
protected Description describeChild(FrameworkMethod method) {
return Description.createTestDescription(getTestClass().getJavaClass(),
testName(method), method.getAnnotations());
}
@Override
protected List<FrameworkMethod> getChildren() {
return computeTestMethods();
}
//
// Override in subclasses
//
/**
* Returns the methods that run tests. Default implementation
* returns all methods annotated with {@code @Test} on this
* class and superclasses that are not overridden.
*/
protected List<FrameworkMethod> computeTestMethods() {
return getTestClass().getAnnotatedMethods(Test.class);
}
@Override
protected void collectInitializationErrors(List<Throwable> errors) {
super.collectInitializationErrors(errors);
validateConstructor(errors);
validateInstanceMethods(errors);
}
private void validateConstructor(List<Throwable> errors) {
validateOnlyOneConstructor(errors);
validateZeroArgConstructor(errors);
}
private void validateOnlyOneConstructor(List<Throwable> errors) {
if (!hasOneConstructor()) {
String gripe= "Test class should have exactly one public constructor";
errors.add(new Exception(gripe));
}
}
/**
* Adds to {@code errors} if the test class's single constructor
* takes parameters
*/
protected void validateZeroArgConstructor(List<Throwable> errors) {
if (hasOneConstructor()
&& !(getTestClass().getOnlyConstructor().getParameterTypes().length == 0)) {
String gripe= "Test class should have exactly one public zero-argument constructor";
errors.add(new Exception(gripe));
}
}
private boolean hasOneConstructor() {
return getTestClass().getJavaClass().getConstructors().length == 1;
}
/**
* Adds to {@code errors} for each method annotated with {@code @Test},
* {@code @Before}, or {@code @After} that is not a public, void instance
* method with no arguments.
*/
protected void validateInstanceMethods(List<Throwable> errors) {
validatePublicVoidNoArgMethods(After.class, false, errors);
validatePublicVoidNoArgMethods(Before.class, false, errors);
validateTestMethods(errors);
if (computeTestMethods().size() == 0)
errors.add(new Exception("No runnable methods"));
}
/**
* Adds to {@code errors} for each method annotated with {@code @Test}that
* is not a public, void instance method with no arguments.
*/
protected void validateTestMethods(List<Throwable> errors) {
validatePublicVoidNoArgMethods(Test.class, false, errors);
}
/**
* Returns a new fixture for running a test. Default implementation executes
* the test class's no-argument constructor (validation should have ensured
* one exists).
*/
protected Object createTest() throws Exception {
return getTestClass().getOnlyConstructor().newInstance();
}
/**
* Returns the name that describes {@code method} for {@link Description}s.
* Default implementation is the method's name
*/
protected String testName(FrameworkMethod method) {
return method.getName();
}
/**
* Returns a Statement that, when executed, either returns normally if
* {@code method} passes, or throws an exception if {@code method} fails.
*
* Here is an outline of the default implementation:
*
* <ul>
* <li>Invoke {@code method} on the result of {@code createTest()}, and
* throw any exceptions thrown by either operation.
* <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
* expecting} attribute, return normally only if the previous step threw an
* exception of the correct type, and throw an exception otherwise.
* <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
* timeout} attribute, throw an exception if the previous step takes more
* than the specified number of milliseconds.
* <li>ALWAYS run all non-overridden {@code @Before} methods on this class
* and superclasses before any of the previous steps; if any throws an
* Exception, stop execution and pass the exception on.
* <li>ALWAYS run all non-overridden {@code @After} methods on this class
* and superclasses after any of the previous steps; all After methods are
* always executed: exceptions thrown by previous steps are combined, if
* necessary, with exceptions from After methods into a
* {@link MultipleFailureException}.
* </ul>
*
* This can be overridden in subclasses, either by overriding this method,
* or the implementations creating each sub-statement.
*/
protected Statement methodBlock(FrameworkMethod method) {
Object test;
try {
test= new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest();
}
}.run();
} catch (Throwable e) {
return new Fail(e);
}
Statement statement= methodInvoker(method, test);
statement= possiblyExpectingExceptions(method, test, statement);
statement= withPotentialTimeout(method, test, statement);
statement= withBefores(method, test, statement);
statement= withAfters(method, test, statement);
return statement;
}
//
// Statement builders
//
/**
* Returns a {@link Statement} that invokes {@code method} on {@code test}
*/
protected Statement methodInvoker(FrameworkMethod method, Object test) {
return new InvokeMethod(method, test);
}
/**
* Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
* has the {@code expecting} attribute, return normally only if {@code next}
* throws an exception of the correct type, and throw an exception
* otherwise.
*/
protected Statement possiblyExpectingExceptions(FrameworkMethod method,
Object test, Statement next) {
Test annotation= method.getAnnotation(Test.class);
return expectsException(annotation) ? new ExpectException(next,
getExpectedException(annotation)) : next;
}
/**
* Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
* has the {@code timeout} attribute, throw an exception if {@code next}
* takes more than the specified number of milliseconds.
*/
protected Statement withPotentialTimeout(FrameworkMethod method,
Object test, Statement next) {
long timeout= getTimeout(method.getAnnotation(Test.class));
return timeout > 0 ? new FailOnTimeout(next, timeout) : next;
}
/**
* Returns a {@link Statement}: run all non-overridden {@code @Before}
* methods on this class and superclasses before running {@code next}; if
* any throws an Exception, stop execution and pass the exception on.
*/
protected Statement withBefores(FrameworkMethod method, Object target,
Statement statement) {
List<FrameworkMethod> befores= getTestClass().getAnnotatedMethods(
Before.class);
return new RunBefores(statement, befores, target);
}
/**
* Returns a {@link Statement}: run all non-overridden {@code @After}
* methods on this class and superclasses before running {@code next}; all
* After methods are always executed: exceptions thrown by previous steps
* are combined, if necessary, with exceptions from After methods into a
* {@link MultipleFailureException}.
*/
protected Statement withAfters(FrameworkMethod method, Object target,
Statement statement) {
List<FrameworkMethod> afters= getTestClass().getAnnotatedMethods(
After.class);
return new RunAfters(statement, afters, target);
}
protected EachTestNotifier makeNotifier(FrameworkMethod method,
RunNotifier notifier) {
Description description= describeChild(method);
return new EachTestNotifier(notifier, description);
}
private Class<? extends Throwable> getExpectedException(Test annotation) {
if (annotation == null || annotation.expected() == None.class)
return null;
else
return annotation.expected();
}
private boolean expectsException(Test annotation) {
return getExpectedException(annotation) != null;
}
private long getTimeout(Test annotation) {
if (annotation == null)
return 0;
return annotation.timeout();
}
}
998

被折叠的 条评论
为什么被折叠?



