如何用spock中的模拟对象替换构造函数注入的对象
How to replace a constructor injected object with mocked object in spock
我知道这个问题很大,但我只是想弄清楚我所处的情况。
我正在开发一个使用来自消息代理的 JMS 消息的应用程序。
我们在消费者端使用骆驼路线。路由构建器中所需的所有对象都是使用 spring 通过构造函数注入注入的。
我想模拟实际处理的行为,一旦消费者从队列中收到消息。所有 类 都通过 spring 配置加载。
下面是三个类:
CustomRouteBuilder.java
public CustomRouteBuilder extends RouteBuilder{
private CustomRouteAdapter customAdapter;
public CustomRouteBuilder (CustomRouteAdapter customAdapter){
this.customAdapter = customAdapter
}
public void configure(RouteDefinition route){
route.bean(customAdapter);
}
}
CustomRouteAdapter.java
public class CustomRouteAdapter {
private Orchestrator orchestrator;
public CustomRouteAdapter (Orchestrator orchestrator){
this.orchestrator = orchestrator;
}
@Handler
public void process(String message){
orchestrator.generate(message) ;
}
}
Orchestrator.java
public class Orchestrator{
private Service service;
public Orchestrator(Service service){
this.service = service;
}
public void generateData(String message){
service.process(message);
}
}
根据我们的要求,我们必须加载此配置文件,然后使用 spock 编写功能测试。
下面是我的
CustomRouteBuilderTest.groovy 文件.
import org.springframework.test.util.ReflectionTestUtils
import spock.lang.Specification
@ContextConfiguration(classes=[CustomRouteBuilderTest.Config.class])
class CustomRouteBuilderTest extends Specification{
private static final String message = "Hello";
Orchestrator orchestrator;
@Autowired
CustomRouteAdapter customRouteAdapter;
def setup(){
orchestrator = Mock(Orchestrator)
ReflectionTestUtils.setField(customRouteAdapter,"orchestrator",orchestrator)
orchestrator.generate(message )
}
private String getMessageAsJson() {
//return json string;
}
private String getMessage() {
// return message;
}
private Map<String, Object> doMakeHeaders() {
//Create message headers
}
private void doSendMessage(){
Thread.sleep(5000)
Map<String,Object> messageHeader = doMakeHeaders()
byte [] message = getMessageAsJson().getBytes()
CamelContext context = new DefaultCamelContext()
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(jmsBrokerUrl)
context.addComponent("activeMQComponent",JmsComponent.jmsComponent(connectionFactory))
ProducerTemplate template = context.createProducerTemplate()
context.start();
template.sendBodyAndHeaders("queueName", message, messageHeader)
}
def "test message consumption"(){
given:
doSendMessage()
}
@Configuration
@Import([FunctionalTestCommonConfig.class,CustomRouteBuilderConfig.class])
@PropertySource(value="classpath:test.properties")
static class Config{
}
}
这里的问题是即使我使用 ReflectionTestUtils 将模拟对象注入适配器,我也无法正确定义它的行为。
当收到消息时,编排器会尝试处理它。
我的要求是:
应该从骆驼路线自动调用适配器,但会发生这种情况
当从适配器调用 orechestrator.generate 时,什么都不应该发生,它应该只是 return。
但这里并没有发生这样的事情。
每次我发送一条消息时,消费者(RouteBuilder)都会收到它并调用处理函数,然后调用
orchestrator.generate(message)
函数,编排器开始处理并从服务级别抛出异常。
任何人都可以帮助我解决这个问题。
我想你的 beans 已经被 Spring 代理了,这个代理使用了 cglib(因为你看到 CustomRouteBuilder$$EnhancerBySpringCGLIB$$ad2783ae
)。
如果确实如此,那么您在测试中没有 @Autowired
CustomRouteAdapter
的真实实例,而是 cglib 代理:Spring 创建了一个新的 class,扩展了真实的class,并覆盖了这个class的所有方法。新方法委托给真实实例。
当您更改 orchestrator
字段时,实际上是在更改代理的 orchestrator
字段,实际实例并未使用该字段。
有多种方法可以实现您的目标:
- 在
CustomRouteAdapter
中添加一个setOrchestrator
方法
- 在您的 spring 配置中创建模拟并让 spring 注入这个模拟而不是 Orchestrator
的真实实例
在真实实例中注入 orchestrator
(丑陋 - 我不建议你这样做,它对代码的可测试性没有帮助!)
customRouteAdapter.targetSource.target.orchestrator = _themock_
我知道这个问题很大,但我只是想弄清楚我所处的情况。 我正在开发一个使用来自消息代理的 JMS 消息的应用程序。 我们在消费者端使用骆驼路线。路由构建器中所需的所有对象都是使用 spring 通过构造函数注入注入的。 我想模拟实际处理的行为,一旦消费者从队列中收到消息。所有 类 都通过 spring 配置加载。 下面是三个类: CustomRouteBuilder.java
public CustomRouteBuilder extends RouteBuilder{
private CustomRouteAdapter customAdapter;
public CustomRouteBuilder (CustomRouteAdapter customAdapter){
this.customAdapter = customAdapter
}
public void configure(RouteDefinition route){
route.bean(customAdapter);
}
}
CustomRouteAdapter.java
public class CustomRouteAdapter {
private Orchestrator orchestrator;
public CustomRouteAdapter (Orchestrator orchestrator){
this.orchestrator = orchestrator;
}
@Handler
public void process(String message){
orchestrator.generate(message) ;
}
}
Orchestrator.java
public class Orchestrator{
private Service service;
public Orchestrator(Service service){
this.service = service;
}
public void generateData(String message){
service.process(message);
}
}
根据我们的要求,我们必须加载此配置文件,然后使用 spock 编写功能测试。 下面是我的
CustomRouteBuilderTest.groovy 文件.
import org.springframework.test.util.ReflectionTestUtils
import spock.lang.Specification
@ContextConfiguration(classes=[CustomRouteBuilderTest.Config.class])
class CustomRouteBuilderTest extends Specification{
private static final String message = "Hello";
Orchestrator orchestrator;
@Autowired
CustomRouteAdapter customRouteAdapter;
def setup(){
orchestrator = Mock(Orchestrator)
ReflectionTestUtils.setField(customRouteAdapter,"orchestrator",orchestrator)
orchestrator.generate(message )
}
private String getMessageAsJson() {
//return json string;
}
private String getMessage() {
// return message;
}
private Map<String, Object> doMakeHeaders() {
//Create message headers
}
private void doSendMessage(){
Thread.sleep(5000)
Map<String,Object> messageHeader = doMakeHeaders()
byte [] message = getMessageAsJson().getBytes()
CamelContext context = new DefaultCamelContext()
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(jmsBrokerUrl)
context.addComponent("activeMQComponent",JmsComponent.jmsComponent(connectionFactory))
ProducerTemplate template = context.createProducerTemplate()
context.start();
template.sendBodyAndHeaders("queueName", message, messageHeader)
}
def "test message consumption"(){
given:
doSendMessage()
}
@Configuration
@Import([FunctionalTestCommonConfig.class,CustomRouteBuilderConfig.class])
@PropertySource(value="classpath:test.properties")
static class Config{
}
}
这里的问题是即使我使用 ReflectionTestUtils 将模拟对象注入适配器,我也无法正确定义它的行为。 当收到消息时,编排器会尝试处理它。 我的要求是: 应该从骆驼路线自动调用适配器,但会发生这种情况 当从适配器调用 orechestrator.generate 时,什么都不应该发生,它应该只是 return。 但这里并没有发生这样的事情。 每次我发送一条消息时,消费者(RouteBuilder)都会收到它并调用处理函数,然后调用
orchestrator.generate(message)
函数,编排器开始处理并从服务级别抛出异常。 任何人都可以帮助我解决这个问题。
我想你的 beans 已经被 Spring 代理了,这个代理使用了 cglib(因为你看到 CustomRouteBuilder$$EnhancerBySpringCGLIB$$ad2783ae
)。
如果确实如此,那么您在测试中没有 @Autowired
CustomRouteAdapter
的真实实例,而是 cglib 代理:Spring 创建了一个新的 class,扩展了真实的class,并覆盖了这个class的所有方法。新方法委托给真实实例。
当您更改 orchestrator
字段时,实际上是在更改代理的 orchestrator
字段,实际实例并未使用该字段。
有多种方法可以实现您的目标:
- 在
CustomRouteAdapter
中添加一个 - 在您的 spring 配置中创建模拟并让 spring 注入这个模拟而不是 Orchestrator 的真实实例
在真实实例中注入
orchestrator
(丑陋 - 我不建议你这样做,它对代码的可测试性没有帮助!)customRouteAdapter.targetSource.target.orchestrator = _themock_
setOrchestrator
方法