【问题标题】:Inject mock into bean failed将模拟注入 bean 失败
【发布时间】:2011-11-13 17:49:44
【问题描述】:

对于我的单元测试,我想对被测 bean 的一些自动装配依赖项使用模拟。模拟已正确创建并注入到单元测试类中,但将其注入到被测 bean 失败

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No matching bean of type [Service] found for
dependency: expected at least 1 bean which qualifies as autowire candidate for this
dependency. Dependency annotations: 
{@org.springframework.beans.factory.annotation.Autowired(required=true)}

这是我使用的 testContext.xml

<bean id="Service" class="org.easymock.EasyMock" factory-method="createMock">
    <constructor-arg  value="Service" ></constructor-arg>
</bean>

<bean id="Controller" class="Controller">
    <property name="Service" ref="Service"></property>
</bean>

这是被测 bean (Controller.java) 中连线失败的部分

@Autowired
private Service service;

但如果我将相同的模拟自动装配到我的单元测试类 (ControllerTest.java) 中,则不会发生错误,

@Autowired
private Service service;

注入的对象是类型

($Proxy18) EasyMock for interface Service

有什么建议为什么不能注入被测 bean 但注入测试有效?

亲切的问候 多米尼克

【问题讨论】:

    标签: spring easymock autowired


    【解决方案1】:

    我认为这个问题可能与事情的顺序有关。在 ControllerTest 中,我猜你正在扩展 AbstractJUnit4SpringContextTests 或类似的东西,它首先连接你的所有 bean(在你甚至有机会创建你的模拟之前)。没有看到你的整个类(或者至少是类声明、setUp、tearDown 和特定的测试方法)很难说。

    单元测试(与集成测试相反)的通常做法是根本不使用 spring 来连接任何东西 - 只需使用属性设置器手动注入您的模拟协作者。集成测试通常不使用模拟——它们使用真实的对象。

    有时,您想进行集成测试,但您想模拟某个特定的合作者,因为真正的合作者会做一些破坏性的事情或发送 SMS 消息或其他东西。在这些情况下,我所做的是让 Spring 按照它想要的方式连接所有内容,然后在所有内容都初始化后使用类中的属性设置器手动注入模拟。除非您要模拟的真实类在其初始化中做了您真正不想要的事情,否则没有问题。

    【讨论】:

    • 感谢您的提示!我不认为在 Spring 中使用模拟是不寻常的。在单元和集成测试期间模拟应用程序的某些层有很多很好的理由。但是你说得对,我们应该注意是否需要单元测试或集成测试。
    • 我不认为在 Spring 应用程序中使用模拟是不寻常的。我只是认为实际使用 Spring 进行模拟注入是不寻常的,因为 Spring 通常也处理 bean 实例化,这正是模拟的核心。但是我已经从我的回答中删除了该评论,因为坦率地说,只是因为我没有看到它,我怎么知道它不寻常?对我来说,这样做似乎更难。
    • 尽管非常感谢您的建议。那天我想了很多,为什么它可能不寻常。最后,我从所有 Spring 上下文链接中减轻了单元测试的负担。现在它甚至都不是从 AbstractJUnit4SpringContextTests 派生的......而且它很漂亮,一种透明性和可理解性进入了测试架构:控制器是用它自己的构造函数创建的,并且 -mocked- 依赖项是通过 ReflectionTestUtil 设置的(由于@Autowired 字段缺少设置器)...这很好,谢谢!
    • 我喜欢你能在测试中发现美!
    • 另外,+1 用于提及 ReflectionTestUtils。我从来没有听说过它,知道它将使我免于在我的被测 Spring 组件上创建所有那些受包保护的 setter 方法。
    【解决方案2】:

    这可能与随机效应有关:Spring 的 bean 实例化顺序。

    查看你的应用上下文配置片段,spring无法知道必须先创建Service类型的mock对象,因为它不知道实例化id为Service的bean会产生Service类型。如果先创建 Controller bean,这可能会阻止自动装配。

    要确认这一点,请尝试

    <bean id="Controller" class="Controller" depends-on="Service">
    

    (编辑:您在服务的 xml 配置中使用自动装配和显式属性。如果您从 Controller.Service 中删除 @Autowired 会发生什么?)

    在提到@jhericks 的旁注中,我们有时会在集成测试中注入模拟对象(在极少数情况下这是绝对必要的):应用程序有一个可选参数(命令行、配置文件等),其中我们可以为它提供一个额外的 xml 应用程序上下文配置文档。这个简短的文档覆盖了相关 bean 的 bean 声明,将其替换为 mock 变体。

    【讨论】:

      猜你喜欢
      • 2020-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-08
      • 2017-08-24
      • 2015-08-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多