【问题标题】:NoSuchBeanDefinitionException for dependencies of mocked beans模拟 bean 的依赖项的 NoSuchBeanDefinitionException
【发布时间】:2012-04-10 16:37:47
【问题描述】:

我正在尝试在集成测试中使用模拟,但运气不佳。我用的是Spring 3.1.1和Mockito 1.9.0,情况如下:

@Component
public class ClassToTest {

    @Resource
    private Dependency dependency;

}

@Component
public class Dependency {

    @Resource
    private NestedDependency nestedDependency;

}

现在,我想使用 Spring 的 JavaConfig 对 ClassToTest 进行集成测试。这是我尝试过的,但它不起作用:

@Test
@ContextConfiguration
public class ClassToTestIntegrationTest {

    @Resource
    private ClassToTest classToTest;

    @Resource
    private Dependency mockDependency;

    @Test
    public void someTest() {
        verify(mockDependency).doStuff();

        // other Mockito magic...

    }


    @Configuration
    static class Config {

        @Bean
        public ClassToTest classToTest() {
            return new ClassToTest();
        }

        @Bean
        public Dependency dependency() {
            return Mockito.mock(Dependency.class);
        }

    }
}

我已经简化了设置以使问题更易于理解。实际上,我有更多的依赖项,只想模拟其中的一些 - 其他的是真实的,基于从我的产品 @Configuration 类导入的配置。

最终发生的事情是我得到一个 NoSuchBeanDefinitionException 说在应用程序上下文中没有 NestedDependency 类型的 bean。我不明白这一点 - 我认为 Spring 会收到 Mockito 的 Dependency 模拟实例,甚至不考虑自动装配它。由于这不起作用,我最终不得不模拟我的整个对象图 - 这完全违背了模拟的意义!

提前感谢您的帮助!

【问题讨论】:

    标签: java spring mocking integration-testing mockito


    【解决方案1】:

    我遇到了同样的问题,但我找到了另一个解决方案。 当 Spring 实例化所有 bean 时,它会检查它是否是 Mockito Mock,在这种情况下,我为注入属性返回 false。要使用它,只需在 Spring 上下文中注入它

    代码如下:

    public class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
    
        private static final MockUtil mockUtil = new MockUtil();
    
        public MockBeanFactory() {
            super();
        }
    
        @Override
        public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
            return !mockUtil.isMock(bean);
        }
    
    }
    

    【讨论】:

    • 太棒了,这让我省了很多心痛!对于想知道如何注入它的任何人,请在您的 Spring 应用程序上下文 XML 中添加这样的一行:
    【解决方案2】:

    Mockito 在模拟类时所做的是它使用 创建一个子类,具有一些花哨的名称,例如:Dependency$EnhancerByMockito (IIRC)。您可能知道,子类从其父类继承字段:

    @Component
    public class Dependency {
    
        @Resource
        private NestedDependency nestedDependency;
    
    }
    
    
    public class Dependency$EnhancerByMockito extends Dependency{
        //...
    }
    

    这意味着 Spring 在使用 mock 时仍然可以看到基类中的字段。你可以做什么:

    1. 使用接口,这将导致 Mockito 使用动态代理而不是 CGLIB 生成的类

    2. 模拟NestedDependency - 我知道它只会将问题更进一步

    3. 禁用 @Resource 注释扫描以进行测试

    【讨论】:

    • Mockito 的 cglib 子类是否也继承了私有字段??
    • @TomMcIntyre:实际上它与 Mockito/CGLIB 无关。如果创建SubDependency extends Dependency 普通类,Spring(通过反射)仍然会找到基类中声明的私有字段。 CGLIB 生成的类也不例外。
    • @Tomasz 您不应该使用 inherit 这个词,因为它可能会被误解。字段和方法保留在其声明类中,如果它们的可见性修饰符允许,则可以在编译时访问。并且在运行时可以通过反射访问当前类或父类中的这些成员。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多