关于写异步代码测试用例的一些思考
作者:网络转载 发布时间:[ 2015/10/12 14:03:15 ] 推荐标签:软件测试技术 测试用例
如果说异步代码不好写是共识的话,那么写异步代码测试用例更难了。近我刚刚完成了一个 Flaky 测试,所以想和大家分享一些关于写异步测试用例的想法。
这篇文章里,我们会探索一个关于异步测试用例的常见问题 —— 如何强制规定某些线程的顺序,如何强制某一个线程操作早于另一些执行。通常我们并不想强行规定线程之间的顺序,因为这违背了多线程的原则,所谓多线程是为了做到并发,从而使得 CPU 可以根据当前资源及应用状态选择佳的执行顺序。但是在测试中,为了确保测试结果的稳定性,又必须明确线程顺序。
测试节流阀(Throttler)
在软件业里节流阀指的是用于限制并发操作个数,预留资源的模式,好比连接池,网络缓存,或者 CPU 密集型操作。和其他同步工具不同的是,节流阀的角色是启动“快速失败”机制,即促使超额请求立即失败,而不是等待。“快速失败”机制之所以重要,是因为切换操作,等待操作会消耗资源 —— 端口,线程,内存等。
以下是一个节流阀的简单实现(基本上是信号量的包装,实际应用中应该是等待,重试等等)
class ThrottledException extends RuntimeException("Throttled!")
class Throttler(count: Int) {
private val semaphore = new Semaphore(count)
def apply(f: => Unit): Unit = {
if (!semaphore.tryAcquire()) throw new ThrottledException
try {
f
} finally {
semaphore.release()
}
}
}
现在我们开始基本的单元测试:测试单线程的节流阀(我们使用测试框架 specs2)。本例里,我们会验证顺序调用是否会超过节流阀的大限制(maxCount变量如下所示)。注意,这里我们用的是单线程,所以我们并不验证节流阀的“快速失败”功能,这里的节流阀都处于不饱和状态。事实上,我们只会测试节流阀在不饱和状态下不会终止操作。
class ThrottlerTest extends Specification {
"Throttler" should {
"execute sequential" in new ctx {
var invocationCount = 0
for (i <- 0 to maxCount) {
throttler {
invocationCount += 1
}
}
invocationCount must be_==(maxCount + 1)
}
}
trait ctx {
val maxCount = 3
val throttler = new Throttler(maxCount)
}
}
测试并发节流阀
前一个例子里,节流阀处于不饱和状态,因为单线程里节流阀一般都不会饱和。下面我们来测试一下多线程环境下节流阀是否还能工作良好。
设置如下:
val e = Executors.newCachedThreadPool()
implicit val ec: ExecutionContext=ExecutionContext.fromExecutor(e)
private val waitForeverLatch = new CountDownLatch(1)
override def after: Any = {
waitForeverLatch.countDown()
e.shutdownNow()
}
def waitForever(): Unit = try {
waitForeverLatch.await()
} catch {
case _: InterruptedException =>
case ex: Throwable => throw ex
}
ExecutionContext 用来构建 Future,waitForever 方法用来持有线程,直到测试结束前的锁释放。接下来的函数里,我们会关闭一个执行服务。
相关推荐
更新发布
功能测试和接口测试的区别
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