java - 使用 @PostConstruct 模拟类会导致初始化失败

标签 java spring unit-testing junit mockito

我有一个如下所示的 java 程序:

public abstract class AbstractA {
  @Autowired
  protected B b;
}

@Component
public class A extends AbstractA {
  private C c;

  @PostConstruct
  public void initilizeC() {
    c = b.getInternalMember();
  }
}

@Component
public class D {
  @Autowired
  private A a;
}

@Component
public class E {
  @Autowired
  private D d;
}

我的测试类如下所示:

@ContextConfiguration(location = {"file:unit-test.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class ETest {
  //class E(being tested) internally uses method of D
  @Autowired
  private A a;

  @Test
  public void methodOfETest() {
    Mockito.when(a.methodOfC(anyInt())).thenReturn(1);
  }

  @After
  public void resetMocks() {
    Mockito.reset(a);
  }
}

我的 spring 文件(仅捕获 bean 初始化)

  <bead id="b" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg="classpath to B" />
  </bean>

  <bead id="a" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg="classpath to A" />
  </bean>

由于以下异常,我的单元测试失败:

    [junit] Failed to load ApplicationContext
     [junit] java.lang.IllegalStateException: Failed to load ApplicationContext
     [junit]     at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
     [junit]     at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
     [junit]     at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
     [junit]     at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
     [junit]     at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:228)
     [junit]     at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:230)
     [junit]     at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
     [junit]     at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
     [junit]     at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:249)
     [junit]     at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
     [junit]     at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
     [junit]     at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
     [junit]     at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
     [junit] Caused by: org.springframework.beans.factory.BeanCreationException: Error
 creating bean with name 'e': Injection of autowired dependencies
 failed; nested exception is
 org.springframework.beans.factory.BeanCreationException: Could not
 autowire field: protected class-path.D class-path.E.d; nested
 exception is org.springframework.beans.factory.BeanCreationException:
 Error creating bean with name 'd': Injection of autowired dependencies
 failed; nested exception is
 org.springframework.beans.factory.BeanCreationException: Could not
 autowire field: private class-path.a class-path.d.a; nested exception
 is org.springframework.beans.factory.BeanCreationException: Error
 creating bean with name 'a': Invocation of init method failed; nested
 exception is java.lang.NullPointerException
     [junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
     [junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
     [junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
     [junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
     [junit]     at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
     [junit]     at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
     [junit]     at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
     [junit]     at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
     [junit]     at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
     [junit]     at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:838)
     [junit]     at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
     [junit]     at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:125)
     [junit]     at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
     [junit]     at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:109)
     [junit]     at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:261)
     [junit]     at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
     [junit]     at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
     [junit] Caused by: org.springframework.beans.factory.BeanCreationException: Could not
 autowire field: protected class-path.D class-path.E.d; nested
 exception is org.springframework.beans.factory.BeanCreationException:
 Error creating bean with name 'd': Injection of autowired dependencies
 failed; nested exception is
 org.springframework.beans.factory.BeanCreationException: Could not
 autowire field: private class-path.a class-path.d.a; nested exception
 is org.springframework.beans.factory.BeanCreationException: Error
 creating bean with name 'a': Invocation of init method failed; nested
 exception is java.lang.NullPointerException
     [junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573)
     [junit]     at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
     [junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
     [junit] Caused by: org.springframework.beans.factory.BeanCreationException: Error
 creating bean with name 'd': Injection of autowired dependencies
 failed; nested exception is
 org.springframework.beans.factory.BeanCreationException: Could not
 autowire field: private class-path.a class-path.d.a; nested exception
 is org.springframework.beans.factory.BeanCreationException: Error
 creating bean with name 'a': Invocation of init method failed; nested
 exception is java.lang.NullPointerException
     [junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
     [junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
     [junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
     [junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
     [junit]     at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
     [junit]     at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
     [junit]     at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
     [junit]     at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
     [junit]     at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1192)
     [junit]     at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
     [junit]     at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
     [junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
     [junit] Caused by: org.springframework.beans.factory.BeanCreationException: Could not
 autowire field: private class-path.a class-path.d.a; nested exception
 is org.springframework.beans.factory.BeanCreationException: Error
 creating bean with name 'a': Invocation of init method failed; nested
 exception is java.lang.NullPointerException
     [junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573)
     [junit]     at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
     [junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
     [junit] Caused by: org.springframework.beans.factory.BeanCreationException: Error
 creating bean with name 'a': Invocation of init method failed; nested
 exception is java.lang.NullPointerException
     [junit]     at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136)
     [junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408)
     [junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570)
     [junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
     [junit]     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
     [junit]     at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
     [junit]     at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
     [junit]     at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
     [junit]     at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
     [junit]     at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1192)
     [junit]     at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
     [junit]     at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
     [junit]     at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
     [junit] Caused by: java.lang.NullPointerException
     [junit]     at class-path.A.initializeC(A.java:64)
     [junit]     at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:354)
     [junit]     at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:305)
     [junit]     at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:133)

根据评论,我可以使用 @Qualifier 并使测试用例正常工作。但我仍然有兴趣了解 @PostConstruct 在模拟时如何工作。

请问您能帮我找到以下问题的答案吗?

  1. @PostConstruct 带注释的方法在模拟时如何工作?
  2. 就我而言,A、B 和 C 类属于不同的包。因此,我需要 mock 他们。 3.1.为什么仅仅 mock A 还不够? 3.2.为什么我应该了解 A 的内部结构并模拟底层/依赖类?

提前致谢!

最佳答案

如果您在上下文加载之前进行模拟,您应该能够做到这一点。像这样的东西

public static class ContextLoader extends GenericXmlContextLoader {
    @Override
    protected void customizeContext(GenericApplicationContext context) {
        super.customizeContext(context);
        // Add mock for A in context here
    }
}

关于java - 使用 @PostConstruct 模拟类会导致初始化失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34606081/

相关文章:

java - 如何在java Enum类中存储Flex文本框值?

spring - 将 Java 8 代码移植到 Java 7

swift - 在 Swift 中对 HKSampleQuery 进行单元测试

ios - Swift 项目的单元测试类

java - Spring PropertyPlaceholderConfigurer 默认值覆盖实际属性值

javascript - 当类实例不可访问时,如何模拟类的特定方法,同时保持所有其他方法的实现?

java - Java Spark如何将JavaPairRDD <HashSet <String>,HashMap <String,Double >>保存到文件?

java - 使用 JPA 坚持时覆盖的字段

java - 如何在spring中创建postmapping方法,以便客户端只需要输入部分对象而不是完整对象?

java - 碰撞检测 - 任何语言