使用@PostConstruct进行模拟会导致初始化失败

时间:2016-01-05 07:13:51

标签: 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和A类; C属于不同的包。因此,我需要嘲笑他们。 3.1。为什么仅仅嘲笑A是不够的? 3.2。我为什么要理解A的内部结构并模拟底层/依赖类?
  3. 提前致谢!

1 个答案:

答案 0 :(得分:0)

如果你在上下文加载之前模拟,你应该可以这样做。像这样的东西

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