Spring Integration - 回复频道

时间:2018-06-12 21:20:54

标签: junit spring-integration

这是Spring Integration Executor Channel using annotations code sample的后续问题。

系统图附有enter image description here

我正试图通过在“公共频道”中发布消息来测试红色突出显示的框。并从信息中设置的REPLY_CHANNEL中读取。

'共同频道'是一个发布订阅频道。 REPLY_CHANNEL是一个QueueChannel。

由于这是一个JUnit测试,我已经模拟了jdbcTemplate,datasource和Impl以忽略任何数据库调用。

我的问题是: 当我将消息发布到'公共频道'时,我在REPLY_CHANNEL上没有收到任何消息。 junit一直在等待回复。

我应该更改什么才能在REPLY_CHANNEL上获得回复?

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(loader = AnnotationConfigContextLoader.class) --------- 1 
@ActiveProfiles("test")
public class QueuetoQueueTest {
    @Configuration
    static class ContextConfiguration { ------------------------------------- 2
        @Bean(name = "jdbcTemplate")
        public JdbcTemplate jdbcTemplate() {
            JdbcTemplate jdbcTemplateMock = Mockito.mock(JdbcTemplate.class);
            return jdbcTemplateMock;
        }

        @Bean(name = "dataSource")
        public DataSource dataSource() {
            DataSource dataSourceMock = Mockito.mock(DataSource.class);
            return dataSourceMock;
        }

        @Bean(name = "entityManager")
        public EntityManager entityManager() {
            EntityManager entityManagerMock = Mockito.mock(EntityManager.class);
            return entityManagerMock;
        }

        @Bean(name = "ResponseChannel")
        public QueueChannel getReplyQueueChannel() {
            return new QueueChannel();
        }

//This channel serves as the 'common channel' in the diagram
        @Bean(name = "processRequestSubscribableChannel")
        public MessageChannel getPublishSubscribeChannel() {
            return new PublishSubscribeChannel();
        }
    }

    @Mock
    DBStoreDaoImpl dbStoreDaoImpl;

    @Test
    public void testDBConnectivity() {
        Assert.assertTrue(true);
    }

    @InjectMocks -------------------------------------------------------------- 3
    StoretoDBConfig storetoDBConfig = new StoretoDBConfig();

    @Autowired
    @Qualifier("ResponseChannel")
    QueueChannel ResponseChannel;

    @Autowired
    @Qualifier("processRequestSubscribableChannel")
    MessageChannel processRequestSubscribableChannel;

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

    @Test
    public void outboundtoQueueTest() {
        try {
            when(dbStoreDaoImpl.storeToDB(any()))
                    .thenReturn(1); ----------------------------------------------- 4

            //create message
            Message message = (Message<String>) MessageBuilder
                    .withPayload("Hello")
                    .setHeader(MessageHeaders.REPLY_CHANNEL, ResponseChannel)
                    .build();

            //send message
            processRequestSubscribableChannel.send(message);
            System.out
                    .println("Listening on InstructionResponseHandlertoEventProcessorQueue");

            //wait for response on reply channel
            Message<?> response = ResponseChannel.receive(); ----------------------- 5
            System.out.println("***************RECEIVED: "
                    + response.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  1. 加载&#39; ContextConfiguration&#39;对于JUnit,以便不访问DB。

  2. 这是按照https://spring.io/blog/2011/06/21/spring-3-1-m2-testing-with-configuration-classes-and-profiles

  3. 在JUnit中加载自定义配置的方法

    在config类中,我们模拟jdbcTemplate,dataSource,entityManager并定义&#39;公共频道&#39;请求发布的位置和ResponseChannel。

    1. 将jdbcTemplate,dataSource模拟注入StoretoDBConfig,以便不会命中数据库

    2. 模拟DaoImpl类,以便忽略数据库调用

    3. 这里的测试块因为REPLY_CHANNEL没有响应

    4. 更新代码:

      Code inside 5 (the class that reads from common channel):
      
          @Configuration
          class HandleRequestConfig {
      
              //Common channel - refer diagram
              @Autowired
              PublishSubscribeChannel processRequestSubscribableChannel;
      
              //Step 9 - This channel is used to send queue to the downstream system
              @Autowired
              PublishSubscribeChannel forwardToExternalSystemQueue;
      
              public void handle() {
                  IntegrationFlows.from("processRequestSubscribableChannel")          // Read from 'Common channel'
                  .wireTap(flow->flow.handle(msg -> System.out.println("Msg received on processRequestSubscribableChannel"+ msg.getPayload())))
                  .handle(RequestProcessor,"validateMessage")                         // Perform custom business logic - no logic for now, return the msg as is   
                  .wireTap(flow->flow.handle(msg -> System.out.println("Msg received on RequestProcessor"+ msg.getPayload())))
                  .channel("forwardToExternalSystemQueue");                           // Post to 'Channel to another system' 
                  }
      
          }
      
          //Code inside step 8 - 'Custom Business Logic' 
          @Configuration
          class RequestProcessor {
              public Message<?> validateMessage(Message<?> msg) {
                  return msg;
              }
          }
      

      我想要实现的目标: 我有针对业务逻辑的各个junit测试用例。我正在尝试测试,当请求被发布到“公共频道”时,响应会在&#39;频道上收到另一个系统&#39;。

      为什么我不能使用原始的ApplicationContext:因为它连接到数据库,我不希望我的JUnit连接到数据库或使用嵌入式数据库。我希望忽略对DB的任何调用。

      我已将回复频道设置为“响应频道”,而不是“自定义业务逻辑”&#39;将其回复发送给ResponseChannel&#39;?

      如果我必须在不同的频道上收听回复,我愿意这样做。我想要测试的是我发送的消息是否是公共频道&#39;通过“通道”接收到其他系统&#39;。

      更新2: 解决Artem的问题。 谢谢Artem的建议。

      是&#39; HandlerRequestConfig&#39;包含在测试配置中? - 我们无法直接调用handle()方法。相反,我想如果我发布了&#39; processRequestSubscribableChannel&#39;,则会调用HandleRequestConfig中的handle()方法,因为它会侦听同一个通道。这是错的吗?我如何测试HandleRequestConfig.handle()方法呢?

      我在HandleRequestConfig的每个步骤的末尾添加了窃听(更新了代码)。我发现没有打印出窃听信息。这意味着我发布的消息甚至没有到达输入通道&#39; processRequestSubscribableChannel&#39;。我做错了什么?

      注意:我尝试删除&#39; processRequestSubscribableChannel&#39; bean内部配置(以便使用applicationContext中的实际&#39; processRequestSubscribableChannel&#39;)。我得到一个不满意的依赖错误 - 预期至少1个bean配置PublishSubscribeChannel。

      更新3:Artem要求发布详细信息。

      @RunWith(SpringRunner.class)
      @SpringBootTest
      public class QueuetoQueueTest  {
      
      //  Step 1 - Mocking jdbcTemplate, dataSource, entityManager so that it doesn't connect to the DB
              @MockBean
              @Qualifier("jdbcTemplate")
              JdbcTemplate jdbcTemplate;
      
              @MockBean
              @Qualifier("dataSource")
              public DataSource dataSource;
      
              @MockBean
              @Qualifier("entityManager")
              public EntityManager entityManager;
      
              @Bean(name = "ResponseChannel")
              public PublishSubscribeChannel getReplyQueueChannel() {
                  return new PublishSubscribeChannel();
              }
      
              //Mocking the DB class
              @MockBean
              @Qualifier("dbStoreDaoImpl")
              DBStoreDaoImpl  dbStoreDaoImpl ;
      
      
              //Inject the mock objects created above into the flow that stores data into the DB.
              @InjectMocks
              StoretoDBConfig storetoDBConfig = new StoretoDBConfig();
      
      //Step 2 - Injecting MessageChannel used in the actual ApplicationContext
              @Autowired
              @Qualifier("processRequestSubscribableChannel")
              MessageChannel processRequestSubscribableChannel;
      
              @Before
              public void setUp() throws Exception {
                  MockitoAnnotations.initMocks(this);
              }
      
              @Test
              public void outboundtoQueueTest() {
              try {
                  when(dbStoreDaoImpl.storeToDB(any()))
                          .thenReturn(1);
      
                  //create message
                  Message message = (Message<?>) MessageBuilder
                          .withPayload("Hello")
                          .build();
                  //send message - this channel is the actual channel used in ApplicationContext
                  processRequestSubscribableChannel.send(message);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
      

      我遇到的错误:代码尝试连接到数据库并抛出错误。

      更新1:StoretoDBConfig中的代码

      @Configuration
      @EnableIntegration
      public class StoretoDBConfig {
          @Autowired
          DataSource dataSource;
      
      /*
       * Below code is irrelevant to our current problem - Including for reference.
       * 
       * storing into DB is delegated to a separate thread.
       *
       *  @Bean
       *  public TaskExecutor taskExecutor() {
       *      return new SimpleAsyncTaskExecutor();
       *  }
       *  
       *  @Bean(name="executorChannelToDB")
       *  public ExecutorChannel outboundRequests() {
       *      return new ExecutorChannel(taskExecutor());
       *  }
       *  @Bean(name = "DBFailureChannel")
       *  public static MessageChannel getFailureChannel() {
       *      return new DirectChannel();
       *  }
       *  private static final Logger logger = Logger
       *              .getLogger(InstructionResponseHandlerOutboundtoDBConfig.class);
      */
          @Bean
          public IntegrationFlow handle() {
          /*
           * Read from 'common channel' - processRequestSubscribableChannel and send to separate thread that stores into DB.
           *
           /
              return IntegrationFlows
                      .from("processRequestSubscribableChannel")
                      .channel("executorChannelToDB").get();
          }
      }
      

      在单独的线上存入DB的代码:

      @Repository
      public class DBStoreDaoImpl implements DBStoreDao {
          private JdbcTemplate jdbcTemplate;
      
          @Autowired
          public void setJdbcTemplate(DataSource dataSource) {
              this.jdbcTemplate = new JdbcTemplate(dataSource);
          }
      
          @Override
          @Transactional(rollbackFor = Exception.class)
          @ServiceActivator(inputChannel = "executorChannelToDB")
          public void storetoDB(Message<?> msg) throws Exception {
                  String insertQuery ="Insert into DBTable(MESSAGE) VALUES(?)";
                  jdbcTemplate.update(insertQuery, msg.toString());
          }
      }
      

1 个答案:

答案 0 :(得分:0)

请向我们展示订阅Common channel的内容。您的图表与您向我们展示的内容无关。您演示的代码不完整。

replyChannel真正需要向其发送消息的真正问题。如果您的流程只是单向 - 发送,存储并且没有任何东西可以返回,那么您确实无法为此获得任何内容。这就是为什么要显示这些通道适配器的原因。

观察消息历程的最佳方法是打开org.springframework.integration类别的调试日志记录。

虽然我看到您在ContextConfiguration中声明了这些频道,但getRequestChannel确实没有任何订阅者。因此,没有人会消费你的信息,当然,没有人会给你回复。

请重新考虑您的测试类以使用真实的应用程序上下文。否则,如果你真的没有测试你的流程,那么完全不清楚你想要达到的目标......