【问题标题】:Autowiring beans implementing same interface - how to autowire a particular dependency bean in a JUnit Test?实现相同接口的自动装配 bean - 如何在 JUnit 测试中自动装配特定的依赖 bean?
【发布时间】:2012-05-11 14:56:27
【问题描述】:

我有一个 Spring 3.1/Java/Tomcat 应用程序。我有一个服务类如下:

public class SomeServiceImpl implements SomeService {

  @Autowired
  public AnotherService anotherService;

  //  other code...

这使用了另一个自动装配的服务类 AnotherService。这两个服务类都在 serviceContext.xml 文件中声明。

我正在编写 junit 来测试 SomeServiceImpl 并使用 autowired 来注入被测类 (SomeService) 以及被测类 (AnotherService) 所需的模拟 (EasyMock) 依赖项。 AnotherService 的 easymock 依赖项在 testContext.xml 中定义,如下所示:

    <bean id="mockAnotherService" class="org.easymock.EasyMock" factory-method="createMock" primary="true">
        <constructor-arg value="com.xyz.AnotherService" />
    </bean>

在我的测试类中,我配置为使用两个上下文文件。但是,我看到 ServiceImpl 已正确连接到实际实现(这是所需的),但不是另一个服务的模拟版本,而是创建版本 (AnotherServiceImpl) 正在连接。

如何改为连接我的依赖项的模拟版本?我不能在实际实现中使用@Resource 或@Qualifier,因为那样会破坏目的。我可以在我的测试课中使用这些。

【问题讨论】:

    标签: spring junit easymock autowired


    【解决方案1】:

    如果它们都在serviceContext.xml 中声明,并且您想模拟其中一个,那么该文件对测试没有用处。要么拆分它,以便您可以使用结合了包含模拟的测试上下文的“真实”Spring上下文文件之一,或者只是手动创建bean并使用setter方法设置AnotherService。控制反转的优点之一是允许您手动创建 bean 并以这种方式注入依赖项。

    【讨论】:

    • 同意,使这更容易的一个好做法是将SomeServiceImpl.anotherService设为私有,在构造函数中对其进行初始化,并通过将构造函数注释为Autowired来使用构造函数注入。然后,当您针对 mock 进行测试时,手动创建 SomeServiceImpl 而不是依赖 Spring。
    【解决方案2】:

    您可以将 serviceContext.xml 导入 testContext.xml,然后只在测试用例中引用 textContext.xml,而不是配置为使用这两个上下文文件。

    当您将配置文件导入另一个文件时,另一个文件有机会覆盖导入文件中的 bean 定义。

    确保模拟的 id 与您的类相同 - 否则它将创建两个不同的 bean。另一个问题是模拟对象没有公布正确的接口类型——因此按类型自动装配可能会失败。要解决该问题,您需要执行以下操作。我将这种方法与 mockito 一起使用 - 也应该适用于简单的 mock。

    <bean id="anotherService" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target">
         <bean class="org.easymock.EasyMock" factory-method="createMock" primary="true">
            <constructor-arg value="com.xyz.AnotherService" />
        </bean>
        </property>
        <property name="proxyInterfaces">
          <value>com.xyz.AnotherService</value>
        </property>
      </bean>
    

    要尝试的另一件事是在您的类中创建 Java Config 样式的方法来创建模拟对象。如果直接在测试类中创建它不起作用,您可以使用 @Configuration 使用另一个类来创建模拟,然后在 testContext.xml 中将其定义为 bean

      @Bean
      AnotherService anotherService() {
          return EasyMock.createMock(AnotherService.class);
      }
    

    【讨论】:

    • 这似乎有效,因为它确实覆盖了具体 bean 并且模拟被连接,但是,当我调用模拟上的任何方法时,如重放、重置、验证它会抛出 java.lang。 IllegalArgumentException:不是模拟:$Proxy34。我猜 EasyMock 无法解析代理类型。
    • 您是否尝试过不使用 ProxyFactoryBean?即,在 testContext.xml 中导入 serviceContext.xml 并将相同的 id 分配给 mock
    猜你喜欢
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    • 2014-03-11
    • 2019-05-08
    • 1970-01-01
    • 2015-11-25
    • 1970-01-01
    • 2022-01-03
    相关资源
    最近更新 更多