关于写异步代码测试用例的一些思考
作者:网络转载 发布时间:[ 2015/10/12 14:03:15 ] 推荐标签:软件测试技术 测试用例
以下是一个测试节流器多线程行为的例子:
"throw exception once reached the limit [naive,flaky]" in new ctx {
for (i <- 1 to maxCount) {
Future {
throttler(waitForever())
}
}
throttler {} must throwA[ThrottledException]
我们创建了 maxCount 个线程(调用 Future{})来调用 waitForever 函数,该函数会一直直到道测试结束。然后我们绕开节流阀执行另一个操作 —— maxCount + 1。预期的行为是,此时应该抛出 ThrottledException 例外。但是,也许预期的例外并不发生,因为接力器的后的一个调用可能会比 future 里的先执行(future 里会抛出例外,但是这不是预期结果)。
上面这个测试的问题是,在像期望中那样节流阀抛出异常然后导致节流阀被违反之前,我们无法确定所有的线程都已经开始并且在 waitForever 函数中被阻塞。为了修复这个问题,我们需要一些方法去等待所有 future 开始。这有一个我们大多数都很熟悉的一种方法:只要增加一个 sleep 函数等待一些合适的时间。
"throw exception once reached the limit [naive, bad]" in new ctx {
for (i <- 1 to maxCount) {
Future {
throttler(waitForever())
}
}
Thread.sleep(1000)
throttler {} must throwA[ThrottledException]
}
好了,现在这个测试几乎都能通过了,但是这个方法还是错的因为下面这两个原因:
测试持续的时间至少会和我们设置好的”合适的时间”差不多久。
在非常罕见的情况下,比如机器处于高负载的时候,这个合适的时间不一定足够。
如果你仍然感到疑惑,可以搜索一下 Google 更多的原因。
一个更好的方式是将我们的线程(future)的开始和我们期望的东西同步起来。我们来使用 java.util.concurrent 里面的 CountDownLatch 类:
"throw exception once reached the limit [working]" in new ctx {
val barrier = new CountDownLatch(maxCount)
for (i <- 1 to maxCount) {
Future {
throttler {
barrier.countDown()
waitForever()
}
}
}
barrier.await(5, TimeUnit.SECONDS) must beTrue
throttler {} must throwA[ThrottledException]
}
我们使用 CountDownLatch 处理障碍同步。这个等待的方法会阻塞主线程直到锁存计数变为 0。随着其它线程的运行(我们把这些其它线程表示为 future),每一个 future 都会调用 countDown 方法使锁存计数减 1。一但计数变为 0,所有的 future 都已经运行到 waitForever 方法中了。
通过那一点,我们可以确保 throttler 是饱和的,内部有大数量(maxCount)的线程。另一个线程试图进入 throttler 将导致异常。我们有一个确定的方式建立我们的测试,测试会有一个主线程进入 throttler。主线程可以恢复到这个点(门闩计数为 0 并等 CountDownLatch 释放等待线程)。
如果一些意想不到的事情发生,我们使用超时略高保障避免无限阻塞发生。如果这样的事情发生,我们的测试失败了。这个超时不会影响到测试时间,除非发生意外情况,否则,我们都不应该等待。
结论
测试异步程序时,通常需要在具体的测试用例中指定多个线程之间的执行顺序。不使用任何同步策略的测试是不可靠的,测试结果有时成功有时失败。使用 Thread.sleep 降低了测试出错的概率,但没有完全解决这个问题。
在大多数情况下,当需要在测试中保证多个线程的执行顺序时,可以使用 CountDownLatch 代替 Thead.sleep。使用 CountDownlatch 的好处是通过它可以指定释放(保持)线程的时机,有两个优点:确保按顺序执行使测试结果更可靠;加快了测试程序的执行速度。即使对于普通的 waiting 操作,比如 waitForever 函数,尽管也可以使用 Thread.sleep(Long.MAX_VALUE) 这样的函数实现,但为了保证程序的健壮性好不要这样做。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南