自动化工具 异步校验工具 awaitility 快速入门

胡刚 · 2017年02月12日 · 最后由 胡刚 回复于 2017年02月21日 · 4749 次阅读

1.背景

在编写自动化测试用例过程中,往往会遇见被测代码有异步或者队列处理的中间过程;如果需要校验这部分结果,必须等待异步操作结束或队列消费完,而这个中间等待的时间是不确定的,常常是根据经验值设定,通过Thread.sleep(经验值),而这个时间通常会设置成最长的那次时间,但是可能 99% 次这个异步操作都低于这个最长的时间,这就造成了每次执行这个测试用例都花费了异步任务最长的那次时间。

现介绍一款开源工具 awaitility:https://github.com/awaitility/awaitility,该工具提供轮询的方式,判断操作是否完成,以最短的时间获取异步任务结果。

2.入门

awaitility 支持 Java、Scala、Groovy,本文以 Java 介绍。

maven 工程在 pom.xml 添加 awaitility 依赖:

<dependency>
      <groupId>org.awaitility</groupId>
      <artifactId>awaitility</artifactId>
      <version>2.0.0</version>
      <scope>test</scope>
</dependency>

其他方式,请参考:
https://github.com/awaitility/awaitility/wiki/Getting_started

3.实例

测试类 import 相关 Class:

import java.util.concurrent.Callable;
import static java.util.concurrent.TimeUnit.*;

import static org.awaitility.Awaitility.*;
import static org.awaitility.Duration.*;
import static org.awaitility.pollinterval.FibonacciPollInterval.*;

构造一个异步任务:

interface CounterService extends Runnable {
    int getCount();
}
// 每隔1s, count累加1
class CounterServiceImpl implements CounterService {
    private volatile int count = 0;
    public void run() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for(int index = 0; index < 5; index++) {
                        Thread.sleep(1000);
                        count += 1;
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }
    public int getCount() {
        return count;
    }
}   

3-1.默认等待时间

await().until(Callable conditionEvaluator) 最多等待 10s 直到 conditionEvaluator 满足条件,否则 ConditionTimeoutException。

@Test
  public void testAsynchronousNormal(){
      final CounterService service = new CounterServiceImpl();
      service.run();
      try{
          // 默认10s, 如果在这时间段内,条件依然不满足,将抛出ConditionTimeoutException
          await().until(new Callable<Boolean>() {
              @Override
              public Boolean call() throws Exception {
                  return service.getCount() == 5;
              }
          });
      } catch (Exception e) {
          Assert.fail("测试代码运行异常:" + e.getMessage() + ",代码位置:" + e.getStackTrace()[0].toString());
      }
  }

默认超时时间为 10s, 可通过 setDefaultTimeout() 自定义默认超时时间。

3-2.最多等待

await().atMost() 设置最多等待时间,如果在这时间内条件还不满足,将抛出 ConditionTimeoutException。

@Test
    public void testAsynchronousAtMost(){
        final CounterService service = new CounterServiceImpl();
        service.run();
        try{
            // 指定超时时间3s, 如果在这时间段内,条件依然不满足,将抛出ConditionTimeoutException
            await().atMost(3, SECONDS).until(new Callable<Boolean>() {
                @Override
                public Boolean call() throws Exception {
                    return service.getCount() == 5;
                }
            });
        } catch (Exception e) {
            Assert.fail("测试代码运行异常:" + e.getMessage() + ",代码位置:" + e.getStackTrace()[0].toString());
        }
    }

3-3.至少等待

await().atLeast() 设置至少等待时间;多个条件时候用 and() 连接。

@Test
   public void testAsynchronousAtLeast(){

       final CounterService service = new CounterServiceImpl();
       service.run();

       try{
           // 指定至少1s, 最多3s, 如果在这时间段内,条件依然不满足,将抛出ConditionTimeoutException
           await().atLeast(1, SECONDS).and().atMost(3, SECONDS).until(new Callable<Boolean>() {
               @Override
               public Boolean call() throws Exception {
                   return service.getCount() == 2;
               }
           });

       } catch (Exception e) {
           Assert.fail("测试代码运行异常:" + e.getMessage() + ",代码位置:" + e.getStackTrace()[0].toString());

       }
   }

3-4.forever 等待

forever 等待,不推荐使用,可能导致死循环。

await().forever().until(new Callable<Boolean>() {
    @Override
    public Boolean call() throws Exception {
        return service.getCount() == 6;
    }
});

3-5.轮询

with().pollInterval(ONE_HUNDRED_MILLISECONDS).and().with().pollDelay(50, MILLISECONDS) that is conditions are checked after 50ms then 50ms+100ms。

@Test
public void testAsynchronousPoll(){
    final CounterService service = new CounterServiceImpl();
    service.run();
    try{
        // 轮询查询,pollInterval每隔多少时间段轮询, pollDelay每次轮询间隔时间
        with().pollInterval(ONE_HUNDRED_MILLISECONDS).and().with().pollDelay(50, MILLISECONDS).await("count is greater 3").until(
                new Callable<Boolean>() {
                    @Override
                    public Boolean call() throws Exception {
                        return service.getCount() == 4;
                    }
                });
    } catch (Exception e) {
        Assert.fail("测试代码运行异常:" + e.getMessage() + ",代码位置:" + e.getStackTrace()[0].toString());
    }
}

3-6.Fibonacci 轮询

with().pollInterval(fibonacci(SECONDS)) 非线性轮询,按照 fibonacci 数轮询。

@Test
   public void testAsynchronousFibonacciPoll(){
       final CounterService service = new CounterServiceImpl();
       service.run();
       try{
           // 使用fibonacci数作为间隔数1,1,2,3,5,8,..., 默认单位milliseconds         with().pollInterval(fibonacci(SECONDS)).await("count is greater 3").until(
                   new Callable<Boolean>() {
                       @Override
                       public Boolean call() throws Exception {
                           return service.getCount() == 4;
                       }
                   });
       } catch (Exception e) {
           Assert.fail("测试代码运行异常:" + e.getMessage() + ",代码位置:" + e.getStackTrace()[0].toString());
       }
   }
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 4 条回复 时间 点赞

挺有意思的库。等待过程中的轮询频次是怎么控制的呢?

也可以试试这个:https://bitbucket.org/csolar/jat 代码会清爽一些:)

学习了

#1 楼 @Lihuazhang 定义 CountDownLatch 变量 latch,并初始化为 new CountDownLatch(1);

启动一个轮询线程,该轮询线程执行体中实现了 while 循环,每次先判断 latch.getCount() 是否为 0,如果为 0,跳出 while 循环;否则,将判断异步结果是否成功的任务提交给线程池 executor 执行,执行体会判断是否成功,成功则 latch.countDown()(导致 latch.getCount() 为 0,下次跳出 while 循环);同时,每次 while 循环执行 Thread.sleep(pollInterval.getValueInMS()); 如果轮询线程执行体 while 循环一直不满足条件,主线程将阻塞 maxTimeoutUnit:latch.await(maxTimeout, maxTimeoutUnit), 如果 latch.getCount() 不为 0,即异步校验不成功,finishedBeforeTimeout 置为 false, finishedBeforeTimeout = latch.await(maxTimeout, maxTimeoutUnit), 输出异常信息。 详情见:https://testerhome.com/topics/7596

恒温 总结 java 版本处理服务端超时策略 中提及了此贴 03月03日 18:48
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册