我有一个Singleton/Factory对象,我想编写一个JUnit测试.Factory方法根据类路径上属性文件中的类名决定实例化哪个实现类.如果未找到属性文件,或者属性文件不包含classname键,则该类将实例化默认实现类.
由于工厂保持Singleton的静态实例在实例化后使用,为了能够在Factory方法中测试"故障转移"逻辑,我需要在不同的类加载器中运行每个测试方法.
有没有办法使用JUnit(或其他单元测试包)来做到这一点?
编辑:这是一些正在使用的Factory代码:
private static MyClass myClassImpl = instantiateMyClass(); private static MyClass instantiateMyClass() { MyClass newMyClass = null; String className = null; try { Properties props = getProperties(); className = props.getProperty(PROPERTY_CLASSNAME_KEY); if (className == null) { log.warn("instantiateMyClass: Property [" + PROPERTY_CLASSNAME_KEY + "] not found in properties, using default MyClass class [" + DEFAULT_CLASSNAME + "]"); className = DEFAULT_CLASSNAME; } Class MyClassClass = Class.forName(className); Object MyClassObj = MyClassClass.newInstance(); if (MyClassObj instanceof MyClass) { newMyClass = (MyClass) MyClassObj; } } catch (...) { ... } return newMyClass; } private static Properties getProperties() throws IOException { Properties props = new Properties(); InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(PROPERTIES_FILENAME); if (stream != null) { props.load(stream); } else { log.error("getProperties: could not load properties file [" + PROPERTIES_FILENAME + "] from classpath, file not found"); } return props; }
AutomatedMik.. 38
这个问题可能已经过时了,但因为这是我遇到这个问题时最接近的答案,但我会描述我的解决方案.
使用JUnit 4
拆分你的测试,以便每个类有一个测试方法(这个解决方案只更改类之间的类加载器,而不是方法之间,因为父类运行器每个类收集一次所有方法)
将@RunWith(SeparateClassloaderTestRunner.class)
注释添加到测试类中.
创建SeparateClassloaderTestRunner
如下所示:
public class SeparateClassloaderTestRunner extends BlockJUnit4ClassRunner { public SeparateClassloaderTestRunner(Class> clazz) throws InitializationError { super(getFromTestClassloader(clazz)); } private static Class> getFromTestClassloader(Class> clazz) throws InitializationError { try { ClassLoader testClassLoader = new TestClassLoader(); return Class.forName(clazz.getName(), true, testClassLoader); } catch (ClassNotFoundException e) { throw new InitializationError(e); } } public static class TestClassLoader extends URLClassLoader { public TestClassLoader() { super(((URLClassLoader)getSystemClassLoader()).getURLs()); } @Override public Class> loadClass(String name) throws ClassNotFoundException { if (name.startsWith("org.mypackages.")) { return super.findClass(name); } return super.loadClass(name); } } }
注意我必须这样做来测试在遗留框架中运行的代码,我无法改变.鉴于选择,我会减少使用静态和/或放入测试挂钩以允许系统重置.它可能不是很漂亮,但它允许我测试很多代码,否则很难.
此解决方案还打破了依赖于类加载技巧的其他任何东西,例如Mockito.
这个问题可能已经过时了,但因为这是我遇到这个问题时最接近的答案,但我会描述我的解决方案.
使用JUnit 4
拆分你的测试,以便每个类有一个测试方法(这个解决方案只更改类之间的类加载器,而不是方法之间,因为父类运行器每个类收集一次所有方法)
将@RunWith(SeparateClassloaderTestRunner.class)
注释添加到测试类中.
创建SeparateClassloaderTestRunner
如下所示:
public class SeparateClassloaderTestRunner extends BlockJUnit4ClassRunner { public SeparateClassloaderTestRunner(Class> clazz) throws InitializationError { super(getFromTestClassloader(clazz)); } private static Class> getFromTestClassloader(Class> clazz) throws InitializationError { try { ClassLoader testClassLoader = new TestClassLoader(); return Class.forName(clazz.getName(), true, testClassLoader); } catch (ClassNotFoundException e) { throw new InitializationError(e); } } public static class TestClassLoader extends URLClassLoader { public TestClassLoader() { super(((URLClassLoader)getSystemClassLoader()).getURLs()); } @Override public Class> loadClass(String name) throws ClassNotFoundException { if (name.startsWith("org.mypackages.")) { return super.findClass(name); } return super.loadClass(name); } } }
注意我必须这样做来测试在遗留框架中运行的代码,我无法改变.鉴于选择,我会减少使用静态和/或放入测试挂钩以允许系统重置.它可能不是很漂亮,但它允许我测试很多代码,否则很难.
此解决方案还打破了依赖于类加载技巧的其他任何东西,例如Mockito.