Mockito/Powermock class 构造通用 class 扩展

Mockito/Powermock class construction for generic class extension

不确定我的问题措辞是否正确,但我正在尝试模拟 class 的构造,该 class 作为泛型传递到我要测试的对象中。下面是我正在测试的对象中发生的事情的示例:

MyClass(boolean historic, Class<? extends Learner> learner) {
    this.learner = learner.newInstance();
    this.historic = historic;
}

Learner 是我要模拟的,它是我构建的各种学习者 classes 的界面。我不想在这里测试他们的逻辑,这就是为什么我想嘲笑他们并控制他们 return。我正在尝试使用以下设置(静态学习器 class 没有构造函数参数):

@Test
@PrepareForTest({MyClass.class, Learner.class, StaticLearner.class})
public void test() {
    Learner mockLearner = Mockito.mock(StaticLearner.class);          
    PowerMockito.whenNew(Learner.class)
            .withNoArguments()
            .thenReturn(mockLearner);

    MyClass myClass = new MyClass(true, StaticLearner.class);
    myClass.process();
}

问题是 Powermock 无法构造学习者,因为它说找不到构造函数。这是有道理的,因为 Learner class 只是一个接口。那么当 StaticLearner 只是从 Learner 继承的泛型时,我该如何模拟传入和构造的 StaticLearner?

这是我收到的错误:

org.powermock.reflect.exceptions.ConstructorNotFoundException: 
No constructor found in class 'com.myco.processing.learners.Learner' with parameter types: [ <none> ].

PowerMockito.whenNew(Learner.class) 替换为 PowerMockito.whenNew(StaticLearner.class)。例如,

    @Test
    @PrepareForTest({MyClass.class})
    public void test() throws Exception {
        StaticLearner mockLearner = Mockito.mock(StaticLearner.class);

        PowerMockito.whenNew(StaticLearner.class)
                .withNoArguments()
                .thenReturn(mockLearner);

        MyClass myClass = new MyClass(true, StaticLearner.class);
        myClass.process();
    }

更新

PowerMockito.whenNew 在使用 new 关键字时有效,例如 new StaticLearner()。如果您使用 StaticLearner.class.newInstance().

它将不起作用
  • 如果要模拟MyClass,将Learner对象的创建委托给新工厂class,LearnerFactory.

    public class LearnerFactory {
    
        public static Learner getInstance(
            Class<? extends Learner> learner) throws IllegalAccessException, InstantiationException {
    
            return learner.newInstance();
        }
    }
    
    public class MyClass {
    
        private boolean historic;
    
        private Learner learner;
    
        public MyClass(boolean historic,
            Class<? extends Learner> learner) throws IllegalAccessException, InstantiationException {
            this.learner = LearnerFactory.getInstance(learner);
            this.historic = historic;
        }
    
        public void process() {
            ...
        }
    }
    
  • 现在模拟工厂 class 到 return 模拟 StaticLearner.

     @RunWith(PowerMockRunner.class)
     @PrepareForTest({MyClass.class, LearnerFactory.class})
     public class MyClassTest {
    
         @Test
         public void test2() throws Exception {             
             StaticLearner mockLearner = 
                 PowerMockito.mock(StaticLearner.class);
             //if needed
             when(mockLearner.doSomething(anyString()))
                 .thenReturn("dummy");
    
             PowerMockito.mockStatic(LearnerFactory.class);
             when(LearnerFactory.getInstance(eq(StaticLearner.class)))
                .thenReturn(mockLearner);
    
             MyClass myClass = new MyClass(true, StaticLearner.class);
             myClass.process();
         }
    
     }
    

显然对 learner.newInstance() 的调用没有参数。 即使有 id,它们也会作为参数传递给构造函数。

那么为什么不直接将 learner.newInstance() 结果 作为参数传递给这个构造函数呢?然后你可以传入一个 "regular" 模拟,你就不需要 PowerMock.