forked from junit-team/junit4
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug: ParentRunner lost test Class from a separate class loader (junit…
…-team#1252) * Bug: ParentRunner lost test Class from a separate class loader When junit.jar located in one ClassLoader but runing tests in another ParentRunner can lost information about run class. For example if use @ClassRule and request test class(org.junit.runner.Description#getTestClass) we can get null, because ParentRunner instead of set Class as is to Description tranform it to class name string, as result org.junit.runner.Description#getTestClass execute Class.forName and can't find test class. Spring-test fail with exception if we try use SpringClassRule ``` java.lang.NullPointerException at org.springframework.test.context.junit4.rules.SpringClassRule.validateSpringMethodRuleConfiguration(SpringClassRule.java:186) at org.springframework.test.context.junit4.rules.SpringClassRule.apply(SpringClassRule.java:134) at org.junit.rules.RunRules.applyAll(RunRules.java:26) at org.junit.rules.RunRules.<init>(RunRules.java:15) at org.junit.runners.ParentRunner.withClassRules(ParentRunner.java:245) at org.junit.runners.ParentRunner.classBlock(ParentRunner.java:194) at org.junit.runners.ParentRunner.run(ParentRunner.java:362) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) ``` As solution, now ParentRunner create Descriptor with explicit specify Class, name and uses annotations. * Restore backward compatibility with sub class of ParentRunner that override getName method * Add ParentRunnerClassLoaderTest to AllClassesTests suite
- Loading branch information
1 parent
8295c93
commit 323353b
Showing
5 changed files
with
161 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
src/test/java/org/junit/tests/running/classes/parent/ParentRunnerClassLoaderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package org.junit.tests.running.classes.parent; | ||
|
||
|
||
import org.junit.Test; | ||
import org.junit.runner.Description; | ||
import org.junit.runner.notification.RunNotifier; | ||
import org.junit.runners.BlockJUnit4ClassRunner; | ||
import org.junit.runners.ParentRunner; | ||
import org.junit.runners.model.FrameworkMethod; | ||
import org.junit.runners.model.InitializationError; | ||
|
||
import java.lang.reflect.Field; | ||
import java.net.URL; | ||
import java.net.URLClassLoader; | ||
import java.util.List; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
|
||
public class ParentRunnerClassLoaderTest { | ||
@Test | ||
public void testClassRuleAccessToClassInAnotherClassLoader() throws Exception { | ||
Class<?> testClassWithOwnClassLoader = wrapToClassLoader(TestWithClassRule.class); | ||
|
||
runTestWithParentRunner(testClassWithOwnClassLoader); | ||
|
||
Field fieldWithReference = testClassWithOwnClassLoader.getDeclaredField("applyTestClass"); | ||
Class<?> usedClass = (Class<?>) fieldWithReference.get(null); | ||
|
||
assertEquals("JUnitRunner can be located in own classLoader, so, " + | ||
"Class.forName org.junit.runner.Description.getTestClass can not see " + | ||
"in current classloader by execute Class.forName", | ||
testClassWithOwnClassLoader, usedClass | ||
); | ||
} | ||
|
||
@Test | ||
public void testDescriptionContainCorrectTestClass() throws Exception { | ||
Class<?> testClassWithOwnClassLoader = wrapToClassLoader(TestWithClassRule.class); | ||
ParentRunner<?> runner = new BlockJUnit4ClassRunner(testClassWithOwnClassLoader); | ||
|
||
Description description = runner.getDescription(); | ||
assertEquals("ParentRunner accept already instantiate Class<?> with tests, if we lost it instance, and will " + | ||
"use Class.forName we can not find test class again, because tests can be " + | ||
"located in different ClassLoader", | ||
description.getTestClass(), testClassWithOwnClassLoader | ||
); | ||
} | ||
|
||
@Test | ||
public void testBackwardCompatibilityWithOverrideGetName() throws Exception { | ||
final Class<TestWithClassRule> originalTestClass = TestWithClassRule.class; | ||
final Class<?> waitClass = ParentRunnerClassLoaderTest.class; | ||
|
||
ParentRunner<FrameworkMethod> subParentRunner = new BlockJUnit4ClassRunner(originalTestClass) { | ||
@Override | ||
protected String getName() { | ||
return waitClass.getName(); | ||
} | ||
}; | ||
|
||
Description description = subParentRunner.getDescription(); | ||
Class<?> result = description.getTestClass(); | ||
|
||
assertEquals("Subclass of ParentRunner can override getName method and specify another test class for run, " + | ||
"we should maintain backwards compatibility with JUnit 4.12", | ||
waitClass, result | ||
); | ||
} | ||
|
||
private void runTestWithParentRunner(Class<?> testClass) throws InitializationError { | ||
ParentRunner<?> runner = new BlockJUnit4ClassRunner(testClass); | ||
runner.run(new RunNotifier()); | ||
} | ||
|
||
private Class<?> wrapToClassLoader(Class<?> sourceClass) throws ClassNotFoundException { | ||
URL classpath = sourceClass.getProtectionDomain().getCodeSource().getLocation(); | ||
VisibleClassLoader loader = new VisibleClassLoader(new URL[]{classpath}, this.getClass().getClassLoader()); | ||
Class<?> testClassWithOwnClassLoader = loader.findClass(sourceClass.getName()); | ||
|
||
assert testClassWithOwnClassLoader != sourceClass; | ||
|
||
return testClassWithOwnClassLoader; | ||
} | ||
|
||
|
||
private static class VisibleClassLoader extends URLClassLoader { | ||
public VisibleClassLoader(URL[] urls, ClassLoader parent) { | ||
super(urls, parent); | ||
} | ||
|
||
@Override // just making public | ||
public Class<?> findClass(String name) throws ClassNotFoundException { | ||
return super.findClass(name); | ||
} | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
src/test/java/org/junit/tests/running/classes/parent/TestWithClassRule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package org.junit.tests.running.classes.parent; | ||
|
||
import org.junit.Assert; | ||
import org.junit.ClassRule; | ||
import org.junit.Test; | ||
import org.junit.rules.TestRule; | ||
import org.junit.runner.Description; | ||
import org.junit.runners.model.Statement; | ||
|
||
import java.lang.reflect.Field; | ||
|
||
/** | ||
* Test class for validate run tests that was load in own ClassLoader | ||
*/ | ||
public class TestWithClassRule { | ||
public static Class<?> applyTestClass; | ||
|
||
@ClassRule | ||
public static TestRule rule = new CustomRule(); | ||
|
||
@Test | ||
public void testClassRuleExecuted() throws Exception { | ||
Assert.assertNotNull("Description should contain reference to TestClass", applyTestClass); | ||
} | ||
|
||
public static final class CustomRule implements TestRule { | ||
|
||
public Statement apply(final Statement base, final Description description) { | ||
return new Statement() { | ||
@Override | ||
public void evaluate() throws Throwable { | ||
Class<?> testClass = description.getTestClass(); | ||
if(testClass != null) { | ||
Field field = testClass.getDeclaredField("applyTestClass"); | ||
field.set(null, description.getTestClass()); | ||
} | ||
base.evaluate(); | ||
} | ||
}; | ||
} | ||
} | ||
} |