从单元测试中注入模拟的Spring@Autowired依赖关系的最佳方法是什么?

import org.springframework.beans.factory.annotation.Autowired;

class MyService {
  @Autowired private DependencyOne dependencyOne;
  @Autowired private DependencyTwo dependencyTwo;

  public void doSomething(){
    //Does something with dependencies
  }
}

在测试这个类时,我基本上有四种方法可以注入模拟依赖关系:

  1. 在测试中使用Spring的ReflectreTestUtils来注入依赖关系
  2. 将构造函数添加到我的服务
  3. 将 setter 方法添加到 MyService
  4. 将依赖项可见性放宽为受包保护,并直接设置字段

哪个是最好的,为什么?

---更新---

我想我应该更清楚一点 - 我只是在谈论“单元”风格测试,而不是Spring“集成”风格测试,其中可以使用Spring上下文连接依赖关系。


答案 1

使用或放置一个 setter。两者都很好。添加构造函数可能会产生副作用(例如,不允许 CGLIB 进行子类化),仅仅为了测试而放宽可见性不是一个好方法。ReflectionTestUtils


答案 2

Spring的 ContextConfiguration可以为您做到这一点。

例如,在下面的测试上下文中,“Local”类是模拟。 是我要测试的类。NotificationService

我正在使用组件扫描将模拟带入上下文,但您也可以轻松使用声明。请注意 use-default-filters=“false” 的使用。<bean>

<context:component-scan base-package="com.foo.config" use-default-filters="false">
    <context:include-filter type="assignable" 
        expression="com.foo.LocalNotificationConfig"/>
</context:component-scan>

<context:component-scan base-package="com.foo.services.notification"
        use-default-filters="false">
    <context:include-filter type="assignable"
        expression="com.foo.services.notification.DelegatingTemplateService"/>
    <context:include-filter type="assignable"
        expression="com.foo.services.notification.NotificationService"/>
</context:component-scan>

<context:component-scan base-package="com.foo.domain"/>

DelegatingTemplateService是一个具有@Delegate的Groovy类。

class DelegatingTemplateService {
  @Delegate
  TemplateService delegate
}

在测试类中,我使用测试上下文并注入服务进行测试。在设置中,我设置了委托模板服务的委托:

@RunWith(classOf[SpringJUnit4ClassRunner])
@ContextConfiguration(Array("/spring-test-context.xml"))
class TestNotificationService extends JUnitSuite {
  @Autowired var notificationService: NotificationService = _
  @Autowired var templateService: DelegatingTemplateService = _

  @Before
  def setUp {
    templateService.delegate = /* Your dynamic mock here */
  }  

在服务中,@Autowired字段是私有的:

@Component("notificationService")
class NotificationServiceImpl extends NotificationService {
  @Autowired private var domainManager: DomainManager = _
  @Autowired private var templateService: TemplateService = _
  @Autowired private var notificationConfig: NotificationConfig = _

推荐