如何在集成测试中等待某些操作

Posted

技术标签:

【中文标题】如何在集成测试中等待某些操作【英文标题】:How to wait in integration test for some operations 【发布时间】:2021-07-05 02:01:31 【问题描述】:

您好,我使用测试容器与 docker 进行了集成测试。在容器上我运行 jms。在测试中,我将消息放入队列。

如何在测试中等待以使其填充到 jms 上?

在本地机器上它可以工作,但在 jenkins 上它失败了,所以我必须添加

    Thread.sleep(3000);

但这很讨厌。 org.awaitility 似乎错过了用法:

await().atMost(2, TimeUnit.SECONDS).until(() -> return true));

我只需要暂停一下以使 jms 传播(放入 jms 队列)并等待侦听器采取行动,即将消息放入数据库。 然后我必须调用 get rest 端点来查看它是否有效。

使用主题会更容易,因为我会在主题上创建测试监听器。 但它是队列,可以有监听器来获取消息。

提前感谢您的任何建议

【问题讨论】:

不知道你为什么叫 sleep nasty,这是一种解决方法,但它确实有效......有没有办法检查 jms 队列是否在你的 await().asMost() 调用中包含一条消息?跨度> 使用sleep() 并不理想,因为它脆弱且效率低下。它很脆弱,因为如果机器速度更慢,那么睡眠时间仍然不够。这是低效的,因为如果机器加速,它将无缘无故地休眠。这真的可以加起来很多测试。 您是使用 Testcontainers 容器定义还是在代码中填充消息? Testcontainers 提供了几种等待策略:testcontainers.org/features/startup_and_waits 以防您将消息作为容器设置的一部分发送 【参考方案1】:

org.awaitility 与JMS QueueBrowser 一起使用,例如:

@Test
public void myTest() throws Exception 
   ...
   await().atMost(2, TimeUnit.SECONDS).until(() -> return queueIsEmpty(queueName)));
   ...


private boolean queueIsEmpty(String queueName) 
   ConnectionFactory cf = new MyBrokersConnectionFactory();
   Connection connection = cf.createConnection();
   Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
   QueueBrowser browser = session.createBrowser(session.createQueue(queueName));
   Enumeration enumeration = senderBrowser.getEnumeration();
   while (enumeration.hasMoreElements()) 
      return false;
   
   return true;

QueueBrowser只读的,因此不会有实际消耗消息的危险。

另一个可能的选择是创建一个具有事务会话的消费者,然后尝试接收消息。如果你确实收到了一条消息,你可以回滚事务并关闭消费者。

【讨论】:

QueueBrowser 不是您可以依赖的东西。 Docs 表示【参考方案2】:

使用重试(例如Spring RetryTemplate 或Failsafe Retry Policy)来缩短集成测试执行时间:

重试 SQL 查询,直到出现记录 重试 REST 端点,直到成功

这里是一个等待DB记录的例子;根据您的需要调整政策:

RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(new FixedBackOffPolicy());
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(
         10, Collections.singletonMap(AssertionError.class, true)));
retryTemplate.execute(retryContext -> 
    List<MyRecord> records = jdbcTemplate.query("select ...");
    Assert.assertEquals(1, records.size());
    return null;
);

【讨论】:

【参考方案3】:

我的解决方案是使用org.awaitility lib 并将断言替换为返回语句:

        await().atMost(30, TimeUnit.SECONDS).until(
            () -> 
                //
                // assertTrue(condition);
                return condition == true;
            

【讨论】:

你实际测试的是什么条件?

以上是关于如何在集成测试中等待某些操作的主要内容,如果未能解决你的问题,请参考以下文章

如何创建一个共享目录以在集成测试和小部件测试之间共享代码?

在java Spring Boot中,如何在集成测试中将内存中的LDAPConnection对象传递给ldapService?

集成测试基本内容概述

如何在 Maven 3 中运行嵌入式 Tomcat 9 以进行集成测试?

在 Rails 中,我应该在 capybara(或集成)测试中包含用户输入表单错误流吗?

Flutter 集成测试 - 状态不佳,无元素