spock 集成测试
探索如何使用Spock 1.2将Spock的模拟和间谍自动注入到Spring上下文中。
Spock中的存根/模拟/间谍(及其生命周期)一直与Spock Specification
类紧密结合。 只能在测试类中创建它们。 因此,使用共享的,预定义的模拟(在单元测试和集成测试中)是有问题的。
这种情况在Spock 1.1中有所改善,但只有在基于Spring的集成测试中使用Spock模拟子系统的全新Spock 1.2(撰写本文时为1.2-RC1)时,与使用@SpringMock
进行Spring @SpringMock
一样容易开机 让我们检查一下。
顺便说一句,除了Spock 1.2-RC1之外,为了更前沿,我将使用Spring Boot 2.1.0.M2,Spring 5.1.0.RC2和Groovy 2.5.2(但所有功能都应在Spring的稳定版本中使用(引导)和Groovy 2.4)。
还有一件事。 为了简单起见,在本文中,我将使用术语“模拟”来指代存根和间谍。 它们的行为有所不同 ,但是在Spock测试中将其注入Spring上下文的范围通常并不重要。
Spock 1.1 –手动方式
多亏了LeonardBrünings的工作,Spock中的模拟才脱离了Specification
类。 最终可以在外部创建它们,然后将其附加到正在运行的测试中。 这是在Spring(或任何其他)上下文中使用Spock模拟的基础。
在此示例代码中,我们有ShipDatabase
类,该类使用OwnShipIndex
和EnemyShipIndex
(当然是由构造函数注入的:))来返回有关所有与名称匹配的已知船只的汇总信息。
//@ContextConfiguration just for simplification, @(Test)Configuration is usually more convenient for Spring Boot tests
//Real beans can exist in the context or not
@ContextConfiguration(classes = [ShipDatabase, TestConfig/*, OwnShipIndex, EnemyShipIndex*/])
class ShipDatabase11ITSpec extends Specification {
private static final String ENTERPRISE_D = "USS Enterprise (NCC-1701-D)"
private static final String BORTAS_ENTERA = "IKS Bortas Entera"
@Autowired
private OwnShipIndex ownShipIndexMock
@Autowired
private EnemyShipIndex enemyShipIndexMock
@Autowired
private ShipDatabase shipDatabase
def "should find ship in both indexes"() {
given:
ownShipIndexMock.findByName("Enter") >> [ENTERPRISE_D]
enemyShipIndexMock.findByName("Enter") >> [BORTAS_ENTERA]
when:
List<String> foundShips = shipDatabase.findByName("Enter")
then:
foundShips == [ENTERPRISE_D, BORTAS_ENTERA]
}
static class TestConfig {
private DetachedMockFactory detachedMockFactory = new DetachedMockFactory()
@Bean
@Primary //if needed, beware of consequences
OwnShipIndex ownShipIndexStub() {
return detachedMockFactory.Stub(OwnShipIndex)
}
@Bean
@Primary //if needed, beware of consequences
EnemyShipIndex enemyShipIndexStub() {
return detachedMockFactory.Stub(EnemyShipIndex)
}
}
}
这些模拟是在单独的类中(在Specification
之外)创建的,因此必须使用DetachedMockFactory
(或使用SpockMockFactoryBean
)。 这些模拟必须被附加(和分离)到测试实例( Specification
实例),但是由spock-spring
模块(从1.1版本开始)会自动处理。 对于从外部创建的通用MockUtil.attachMock()
,还需要使用MockUtil.attachMock()
和mockUtil.detachMock()
使其起作用。
结果,可以在Spring上下文中创建和使用模拟,但是它不是很方便,也不常用。
Spock 1.2 –一流的支持
Spring Boot 1.4通过(Mockito)的模拟为集成测试带来了新的质量。 它利用了最初于2012年在Springockito中提出的想法(当时Spring配置主要是用XML编写的:))将模拟(或间谍)自动注入到Spring(引导)上下文中。 Spring Boot团队扩展了这个想法,并且由于有了它作为内部支持的功能(通常),因此只需在测试中添加一个或两个注释即可可靠地工作。
Spock 1.2中内置了类似的基于注释的机制。
//@ContextConfiguration just for simplification, @(Test)Configuration is usually more convenient for Spring Boot tests
//Real beans can exist in the context or not
@ContextConfiguration(classes = [ShipDatabase/*, OwnShipIndex, EnemyShipIndex*/])
class ShipDatabaseITSpec extends Specification {
private static final String ENTERPRISE_D = "USS Enterprise (NCC-1701-D)"
private static final String BORTAS_ENTERA = "IKS Bortas Entera"
@SpringBean
private OwnShipIndex ownShipIndexMock = Stub() //could be Mock() if needed
@SpringBean
private EnemyShipIndex enemyShipIndexMock = Stub()
@Autowired
private ShipDatabase shipDatabase
def "should find ship in both indexes"() {
given:
ownShipIndexMock.findByName("Enter") >> [ENTERPRISE_D]
enemyShipIndexMock.findByName("Enter") >> [BORTAS_ENTERA]
when:
List<String> foundShips = shipDatabase.findByName("Enter")
then:
foundShips == [ENTERPRISE_D, BORTAS_ENTERA]
}
}
没有太多要添加的内容。 @SpringBean
指示Spock将模拟注入到Spring上下文中。 类似地, @SpringSpy
用间谍包装真实的bean。 在@SpringBean
的情况下,需要初始化一个字段以让Spock知道我们打算使用存根还是模拟。
另外,还有一个更通用的注释@StubBeans
,以存根替换所有已定义的bean。 但是,我计划在另一篇博客文章中单独介绍它。
局限性
对于那些希望在本文演讲后立即在您的Spock测试中将所有Mockito的模拟重写为Spock的模拟的人来说,这是一个警告。 Spock的模拟物-由于其性质和与Specification
关系-具有某些局限性。 在幕后的实现创建了一个代理,该代理被注入到Spring上下文中(可能)替换实际的bean(存根/模拟)或包装它们(间谍)。 该代理在特定测试(规范)类中的所有测试之间共享。 实际上,在Spring能够缓存上下文的情况下,它也可以跨越具有相同bean / mock声明的其他测试(与Mockito的模拟或一般的Spring集成测试类似的情况)。
但是,真正重要的是,代理在执行之前就已连接到测试,并在执行之后被分离。 因此,实际上,每个测试都有其自己的模拟实例(不能应用于@Shared
字段),例如,将来自不同测试的交互分组并一起验证它们是有问题的(通常是非常明智的,但可能会导致某些情况)复制)。 但是,使用setup
块(或在线存根)可以共享存根和交互预期。
摘要
Spock 1.2最终带来了轻松的Spock存根/模拟/间谍支持,以便在Spring上下文中使用它们,这与Spring Boot中为Mockito提供的支持相当。 将spock-spring
模块添加到项目(运行时)依赖项就足够了。 尽管有一些限制,但在Spock(集成)测试中将本机Spock的模拟子系统与外部模拟框架(例如Mockito)混合起来要少一点。 不错的是,它也应该在普通的Spring Framework测试中起作用(不仅是Spring Boot测试)。 Guice已实现了相同的功能(但我尚未对其进行测试)。
此外,Spock 1.2还带来了其他一些更改,包括对Java 9+的更好支持,值得在您的测试套件中进行尝试(当然,请报告任何可能发现的回归错误:))。
还有一个好消息。 除了使Spock 1.2成为可能的伦纳德的工作以及大量的错误报告者和PR贡献者之外,最近以来,还有其他一些提交者正在致力于使Spock变得更好。 您可能从其他一些流行的FOSS项目中了解了其中一些。 而且,Spock 1.2 (初步)计划成为基于JUnit 4的最后一个版本,而下一个稳定的Spock版本可能是2.0,这是利用JUnit 5及其(以及其他)并行运行测试的本机能力。
这些示例是使用Spock 1.2-RC1编写的。 一旦发布,它将更新为1.2-final。 源代码可从GitHub获得。
翻译自: https://www.javacodegeeks.com/2018/09/spock-spring-beans-mocking-integration.html
spock 集成测试