surefire 拉起 junit 单元测试类 源码阅读(一)

根据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();
	}
}






评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值