Spring 集成 Java DSL 单元测试 - 如何模拟服务激活器 class 或其他 components/endpoints?

Spring Integration Java DSL unit test - How to mock a Service Activator class or other components/endpoints?

我有一个 class,其中包含如下几个服务激活器方法:

@MessageEndpoint
public class TestService {
    @ServiceActivator
    public void setComplete(Message<String> message){
        //do stuff
    }
}

在集成流程中,其中一个渠道调用了以下方法之一:

@Bean
public TestService testService() {
    return new TestService();
}

@Bean
public IntegrationFlow testFlow() {
    return IntegrationFlows.from("testChannel")
            .handle("testService", "setComplete")
            .handle(logger())
            .get();
}

我正在为此流程编写单元测试并使用 Mockito 来 mcoking 服务激活器 class:

@ContextConfiguration(classes = IntegrationConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class AppTest {

    @Mock
    private TheGateway startGateway;

    @Mock
    private TestService testrvice;


    @Autowired
    @Qualifier("testChannel")
    DirectChannel testChannel;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test()
    public void testMessageProducerFlow() throws Exception {

        Mockito.doNothing().when(startGateway).execute("test");
        startGateway.execute("test");
        Mockito.verify(startGateway).execute("test");
        TestChannel.send(new GenericMessage<>("test"));
        Mockito.verify(testService).setComplete(new GenericMessage<>("test"));

    }
}

当我不模拟 TestService 时,它​​会毫无问题地执行流程。 任何关于如何模拟服务激活器 class 的指导都会有所帮助。

更新: 当我模拟它时(如上面的代码片段所示),它不会调用模拟对象,而是执行实际的东西,最后一行 Mockito.verify(testService)... 断言从未调用模拟 testService。

首先你误解了Spring测试框架的工作原理。

  1. @ContextConfiguration(classes = IntegrationConfig.class) 按原样加载配置,不做任何修改,并根据该配置启动应用程序上下文。

  2. 根据第一个条件,您的 .handle("testService", "setComplete") 使用 testService() @Bean 而不是 @Mock

  3. 只有在测试 applicationContext 启动后,所有这些 @Mocks 和 @Autowireds 才开始工作。

换句话说,您的模拟不会改变原始 IntegrationConfig 中的任何内容。

在框架中使用反射来检索特定 bean 的某些字段以将其替换为模拟。但这并不容易。

我建议您区分集成和服务配置,并使用两种不同的 classes 进行生产和测试。像这样:

  1. 必须将 testService() @BeanIntegrationConfig 移动到新的 @Configuration class 以进行生产。

  2. TestServiceConfig 可能如下所示:

    @Bean
    public TestService testService() {
        return Mockito.mock(new TestService());
    }
    
  3. 最后你的AppTest应该修改成这样:

    @ContextConfiguration(classes = {IntegrationConfig.class, TestServiceConfig.class})
    ....
    @Autowired
    private TestService testrvice;
    

这一切只是因为应用程序上下文和单元测试范围处于不同级别。