摘要:本文记录一下如何在单元测试中进行并发测试。本文主要介绍3种方法,其中一个是使用第三方的依赖,另外2个是 Spockframework 提供的工具。
有时候我们需要在单元测试中测试并发,主要的场景有如下几种
- 测试的目标启动了新的线程,同时有回调函数,需要在单元测试中测试其回调函数是否执行。
- 测试中启动了多个线程
- 测试需要等待异步操作的结果
如果有如上需求的单元测试,需要引入并发测试的支持。
concurrentunit.Waiter
这个第三方库设计的很简洁,功能比较全面,其用法十分简单,只需要在主线程通过 waiter.await(10L,2)
阻塞等待子线程和回调,在回调或子线程中通过 waiter.resume()
取现主线程的阻塞。
其中 waiter.await(time,count)
第一个参数是等待的时间,也就是说超过这个时间还没有子线程或回调调用 resume
则会报超时的异常。第二个参数是等待 resume
的个数,比如我们需要测试一个任务结束后,需要回到两个函数,那么我们就需要接受2个 resume
才能让主线程继续,否则还是会超时错误。
案例
1 | def "test Post data with adding task listeners"() { |
BlockingVariable
这个是 spockframework
内置的工具类,位于spock.util.concurrent
包中。
Class BlockingVariable
T - the variable’s type
A statically typed variable whose get() method will block until some other thread has set a value with the set() method, or a timeout expires. Useful for verifying state in an expect- or then-block that has been captured in some other thread.
通过名字我们可以猜出,该方法通过泛型将我们的业务类包装成一个可以被阻塞的变量。也就是说这个变量可以被阻塞,直到被赋值。通俗的讲就是如果我希望在某个子进程或者回调中向某个变量赋值,这时候我们就可以用这个BlockingVariable
将该变量进行包装,然后在主线程通过 xxx.get()
操作阻塞直到 xxx.set()
操作被执行。同样该方法也可以设置超时时间和时间单位:
BlockingVariable(int timeout, TimeUnit unit)
案例
1 | def "test Post data with adding task listeners by using BlockingVariable"() { |
AsyncConditions
上面的spockframework
提到的方法比较简单,一般是针对一个变量来测试。AsyncConditions
提供更多个测试空间。
Class AsyncConditions
Alternative to class BlockingVariable(s) that allows to evaluate conditions in a thread other than the spec runner’s thread(s). Rather than transferring state to an expect- or then-block, it is verified right where it is captured. On the upside, this can result in a more informative stack trace if the evaluation of a condition fails. On the downside, the coordination between threads is more explicit (number of evaluate blocks has to be specified if greater than one), and the usual structure of a feature method cannot be preserved (conditions are no longer located in expect-/then-block).
该工具有点类似于第一种方法,首先定义一个 AsyncConditions
对象,然后在主线程 conds.await()
阻塞,最后在子线程进行 conds.evaluate
来进行测试目标变量是否达到预期。同样在await
的时候可以设置超时时间:
await(double seconds)
案例
1 | def "test Post data with adding task listeners by using AsyncConditions"() { |