DTeam 团队日志

Doer、Delivery、Dream

Vert.x入坑须知(4.1):用Spock写异步测试

胡键 Posted at — Dec 29, 2018 阅读

点进来的老读者可能会对系列的数字感到奇怪:为何不是 5 而是 4.1?实话是,本篇内容足够重要,可以算得上入坑必须了解的内容。但从篇幅上来讲它实在又有点单薄,而我最近也没时间憋个大招去写长篇大论,权衡之下干脆采用小数计位,赶在 18 年最后一个工作日完稿。

之前在本系列的第三篇,我曾提到我们团队采用的测试框架是 Spock,也提到了为何我们坚持使用这个框架,即使是在 Vert.x 已经对测试有支持的情况下。

对于非异步场景,Spock 工作得很好,写测试的效率也很高。对于异步场景,之前我们一直采用的策略是加上“必要的 sleep”。就这样凑合了不短的时间之后,终于团队内有小伙伴开始抱怨了。

这也很容易理解,由于机器配置的不同,sleep 的时间很难把握得恰到好处。往往出现在一台机器上可以跑过的测试而在另一台机器上跑不过,再加上我们要求必须有自动化测试代码并配置了 pipeline,这样一来自然就有人受不了啦。

本着解决根本问题的精神,再辅以高超的搜索大法,最终我们摸索出了一套用 Spock 来测试 vertx 异步场景的方法。说来惭愧,这个方法一直就在那里,只是我们一直没有特别重点的去关注:Spock 对于异步测试的支持。

对于异步场景,Spock 已经提供了 3 个测试辅助类:

BlockingVariable

顾名思义,这个类的作用就是一直阻塞,直到有值。它非常适合测试异步回调返回值的测试场景,如下例,测试一个数据库的返回值。

when:
BlockingVariable<Integer> rowCount = new BlockingVariable<>()
BlockingVariable<String> callback = new BlockingVariable<>()
pgUtils.simpleSql(NamedQuery.uncalledCallback) { rowSet ->
    rowCount.set(rowSet.size())
    callback.set(rowSet.asList()[0].getString('callback'))
}

then:
rowCount.get() == 1
callback.get() == 'callback2'

rowCount.get()会一直阻塞,直到其检测到有值,这样就避免的无谓的 sleep。

AsyncConditions

这个类用于测试异步条件是否,如下面的例子:

setup:
conditions = new AsyncConditions(1)  // 其中的数字为需要evaluate的个数,缺省为1
String source = "foo.txt";
String target = "bar.txt";
createFileWithJunk(source, 100);

when:
vertx.fileSystem().copy("$testDir$pathSep$source", "$testDir$pathSep$target") {
    conditions.evaluate {
        assert new File(testDir, source).exists()
        assert new File(testDir, target).exists()
    }
}

then:
conditions.await();

在这个例子中,await 将一直处于阻塞状态,直到 evaluate 部分的代码满足条件。但它的缺点也很明显,整个测试并不那么“spock style”,整个验证过程都放在了 when 部分。

PollingConditions

这个类的作用跟 AsyncConditions 非常类似,但它弥补了后者的不足,如下例:

setup:
def conditions = new PollingConditions(timeout: 10, initialDelay: 1.5, factor: 1.25)
def machine = new Machine()

when:
machine.start()

then:
conditions.eventually {
    assert machine.temperature >= 100
    assert machine.efficiency >= 0.9
}

从上面的代码可以看出来,验证部分就在 then 部分,完美。

总结

至此,Spock 的这些工具类非常好地解决了 vertx 的异步场景测试问题,而且 gradle 输出的测试报告来看,测试时间也比之前要省不少!

那么,这些工具类也可以用于测试其他的异步场景么?当然,从上面的代码看,所有测试只依赖 Spock 的异步测试工具类,与 vertx 完全无关!并且,对于每个测试工具类,Spock 也提供了超时机制,具体细节可以参看它们的 JavaDoc