繁体   English   中英

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

[英]What is the best way to inject mocked Spring @Autowired dependencies from a unit test?

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的ReflectionTestUtils来注入依赖项
  2. 向MyService添加构造函数
  3. 将setter方法添加到MyService
  4. 放宽对包受保护的依赖性可见性并直接设置字段

哪个最好,为什么?

---更新---

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

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

Spring的ContextConfiguration可以为您完成此任务。

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

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

<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
}

在测试类中,我使用测试上下文并注入要测试的服务。 在设置中,我设置DelegatingTemplateService的委托:

@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 = _

2)使用@Autowired构造函数注入(如果那是选项2;否则,选择5)

在有效状态下创建对象的正确构造函数是一种更正确的面向对象的方法,丢失cglib代理是相对不重要的,因为我们无论如何都要对接口进行编码,对吧?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM