为什么PowerMock PreparFortest防止模拟注射



我的代码如下:

public class RealWorldBoImpl extends AbstractBoImpl<T> implements SomeBo{}

@RunWith(PowerMockRunner.class)
@PrepareForTest({RealWorldBoImpl.class})
public class RealWorldBoImplTest {
    @InjectMocks
    private RealWorldBoImpl realWorldBo;
    @Mock
    private RealWorldDAO realWorldDAO;
    @Test
    public void changeStatusMainSubString() throws Exception {
        long id = 1L;
    }

在这种情况下,Realworlddao不能注入Realworldbo。但是当我删除preparfortest时,它可以正常工作。

我也尝试了其他课程,它们效果很好。似乎Realworldboimpl是特殊的,当做准备时,它不会正确注入模拟。

我调试了此代码,并发现,在org.mockito.internal.util.util.reflection.fieldinitializer#checkParametrized,constructor.getParametertypes()不是空的,并且具有构造函数,并带有类indionaleleloadclass。

private void checkParameterized(Constructor<?> constructor, Field field) {
        if(constructor.getParameterTypes().length == 0) {
            throw new MockitoException("the field " + field.getName() + " of type " + field.getType() + " has no parameterized constructor");
        }
    }

但我不知道Realworldboimpl有什么特别之处。它只是扩展了父类并实现接口。这有关系吗?

当PowerMock准备一个具有Object以外的超级阶级的类时,它将构造函数添加到类中,该类构建函数以org.powermock.core.IndicateReloadClass类型的参数。

为什么PowerMock这样做?PowerMock通过这种机制实现了超类构造函数。在您的情况下,因为RealWorldBoImpl源自AbstractBoImpl PowerMock将以下构造函数添加到RealWorldBoImplAbstractBoImpl类:

  public RealWorldBoImpl(IndicateReloadClass var1) {
    super(var1);
  }
  public AbstractBoImpl(IndicateReloadClass var1) {
    super(); //assuming the parent class is Object otherwise super(var1)
  }

并将RealWorldBoImpl的默认NO-ARG构造函数更改为以下:

  public RealWorldBoImpl() {
    Object var1 = MockGateway.constructorCall(Desc.getClazz("org.example.RealWorldBoImpl"),
        new Object[0], Desc.getParams("()V"));
    if (var1 != MockGateway.PROCEED) {
      super((IndicateReloadClass)null);
    } else {
      super();
    }
  }

那是PowerMock部分,现在让我们进入Mockito部分。

Mockito具有两种注入策略(MockInjectionStrategy):ConstructorInjectionPropertyAndSetterInjection

ConstructorInjection使用构造函数注入模拟,如果注射者至少具有一个非默认构造函数(一种至少采用一个参数的构造函数),则使用。如果注射者只有一个no-arg构造函数,则Mockito使用PropertyAndSetterInjection使用Setter方法,如果没有Setter方法,则它可以通过反射设置字段的值直接注入模拟。

在您准备RealWorldBoImpl类时,您的构造函数带有一个参数,Mockito使用ConstructorInjection通过PowerMock添加的构造函数将模拟注入对象(并且由于没有类型IndicateReloadClass的模拟,Mockito将NULL传递到NULL构造函数,但这并不重要,因为构造函数什么都不做),因此没有模拟。

那么如何解决问题?如果您要注入多个模拟,请在Injextee类中添加一个构造函数,并具有与要注入的模拟数量一样多的参数。只要您拥有一个多个参数的构造函数,注射剂就可以正常工作,否则您的注射者类应该具有Object作为超类。

如果您只有一个模拟,则可以向构造函数添加一个虚拟参数,以使Mockito选择您的构造函数,而不是PowerMock添加的构造函数:

public RealWorldBoImpl(RealWorldDAO realWorldDAO, String dummy) {
    this.realWorldDAO = realWorldDAO;
}

最新更新