如何模拟Class.forName和class.Constructor.newInstance()

时间:2016-09-01 19:14:27

标签: java unit-testing

我一直试图模仿

clazz = Class.forName("Complete Class path") 
Object obj = clazz.Constructor().getInstance();

我用Google搜索,并找到了如何使用powermock模拟静态方法的链接。我尝试使用这些逻辑来模拟

Class.forName("Complete Class path")

Object obj = clazz.Constructor().getInstance()但一直面临错误。

at sun.reflect.GeneratedSerializationConstructorAccessor16.newInstance(Unknown Source) [junit]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) [junit] 
at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:45) [junit]
at org.powermock.reflect.internal.WhiteboxImpl.newInstance(WhiteboxImpl.java:223) [junit]
at org.powermock.reflect.Whitebox.newInstance(Whitebox.java:139) [junit]
at org.powermock.api.mockito.internal.mockcreation.MockCreator.mock(MockCreator.java:64) [junit]
at org.powermock.api.mockito.PowerMockito.mock(PowerMockito.java:203) [junit]
at org.powermock.api.extension.listener.AnnotationEnabler.standardInject(AnnotationEnabler.java:106) [junit]
at org.powermock.api.extension.listener.AnnotationEnabler.beforeTestMethod(AnnotationEnabler.java:54) [junit]
at org.powermock.tests.utils.impl.PowerMockTestNotifierImpl.notifyBeforeTestMethod(PowerMockTestNotifierImpl.java:90) [junit]
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:292) [junit]
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:127) [junit]
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82) [junit]
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282) [junit]
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87) [junit]
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50) [junit]
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207) [junit]
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146) [junit]
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120) [junit]
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34) [junit]
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44) [junit]
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:122) [junit]
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:106) [junit]
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)

2 个答案:

答案 0 :(得分:2)

我不相信嘲笑Class是一个好主意,你应该重写你的代码以使其更易于测试。您应该将组件创建委托给工厂,然后很容易模拟它。

以下是您的工厂的样子:

public interface MyObjectFactory {
    MyObject create();
}

然后,在您需要创建此对象的类中,将工厂添加为字段

public class MyClass {
    // The factory that will create my objects
    private MyObjectFactory factory;

    public void someMethod() {
        // Calls the factory to create my object 
        MyObject object = factory.create();
        ...
    }
}

完成后,您可以使用Mockito模拟MyObjectFactory,以便提供您选择的MyObject个实例。

测试案例将是:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    MyObjectFactory factory;
    @InjectMocks
    MyClass myClass = new MyClass();

    @Test
    public void testSomeMethod() {
        ...
        Mockito.when(factory.create()).thenReturn(myObject);
        // Call the method
        myClass.someMethod();
    }
}

答案 1 :(得分:1)

由于早期forName加载这一事实,我认为你无法通过模拟静态Class方法取得成功。我不知道可以做到这一点的模拟框架。

但是,如果您正在测试自己的代码,那么我建议您将Class.forName逻辑移到此strategy

interface ClassLoadingStrategy {
    ClassLoadingStrategy DEFAULT = Class::forName;

    Class<?> forName(final String name) throws Exception;
}

重写您的代码,以便在需要Class.forName的地方使用它,并在测试中将其替换为mock。