感興趣的朋友,可以關(guān)注微信服務(wù)號(hào)“猿學(xué)堂社區(qū)”,或加入“猿學(xué)堂社區(qū)”微信交流群
版權(quán)聲明:本文由作者自行翻譯叠洗,未經(jīng)作者授權(quán),不得隨意轉(zhuǎn)發(fā)旅东。
到目前為止灭抑,我們已經(jīng)開發(fā)了未包含測試的Wiki實(shí)現(xiàn)。這自然不是一種好的實(shí)踐抵代,因此讓我們看一下如何為Vert.x代碼編寫測試腾节。
5.1 開始
vertx-unit模塊提供了工具來測試Vert.x中的異步操作。除此之外荤牍,你還可以使用你選擇的測試框架案腺,如JUnit。
使用JUnit康吵,需要的Maven依賴如下:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-unit</artifactId>
<scope>test</scope>
</dependency>
JUnit測試需要注解為VertxUnitRunner runner劈榨,以便使用vertx-unit特性:
@RunWith(VertxUnitRunner.class)
public class SomeTest {
// (...)
}
通過這個(gè)runner,JUnit測試和生命周期方法接收一個(gè)TestContext參數(shù)涎才。這個(gè)對(duì)象提供了對(duì)基礎(chǔ)斷言鞋既、存儲(chǔ)數(shù)據(jù)上下文的訪問,以及幾個(gè)面向異步的幫助類耍铜,我們將在本節(jié)中可以看到。
為了說明它跌前,讓我們考慮一個(gè)異步場景棕兼,我們想要檢查一個(gè)timer任務(wù)被調(diào)用了一次,以及一個(gè)周期任務(wù)被調(diào)用了三次抵乓。由于這個(gè)代碼是異步的伴挚,所以測試方法在測試完成之前就退出,因此灾炭,使測試通過或失敗還需要以異步方式進(jìn)行:
@Test /*(timeout=5000)*/ ⑧
public void async_behavior(TestContext context) { ①
Vertx vertx = Vertx.vertx(); ②
context.assertEquals("foo", "foo"); ③
Async a1 = context.async(); ④
Async a2 = context.async(3); ⑤
vertx.setTimer(100, n -> a1.complete()); ⑥
vertx.setPeriodic(100, n -> a2.countDown()); ⑦
}
① TestContext是由runner提供的一個(gè)參數(shù)茎芋。
② 由于我們?cè)趩卧獪y試中,我們需要?jiǎng)?chuàng)建一個(gè)Vert.x上下文蜈出。
③ 此處是一個(gè)基本的TestContext斷言的示例田弥。
④ 我們得到第一個(gè)Async對(duì)象,它接下來可能完成(或失斦≡)偷厦。
⑤ 這個(gè)Async對(duì)象作為一個(gè)countdown工作商叹,在三次調(diào)用之后成功完成。
⑥ 我們?cè)趖imer觸發(fā)時(shí)完成只泼。
⑦ 每次周期任務(wù)觸發(fā)一個(gè)countdown剖笙。測試在所有Async對(duì)象完成后通過。
⑧ 有一個(gè)默認(rèn)(long)超時(shí)時(shí)間用于異步測試用例请唱,但是它可以通過JUnit的@Test注解覆蓋弥咪。
5.2 測試數(shù)據(jù)庫操作
數(shù)據(jù)庫服務(wù)非常適合編寫測試。
我們首先需要部署數(shù)據(jù)庫Verticle十绑。我們將配置JDBC鏈接為HSQLDB酪夷,使用內(nèi)存存儲(chǔ),當(dāng)成功的時(shí)候孽惰,我們將獲取一個(gè)服務(wù)代理用于我們的測試用例晚岭。
由于這些操作的需要,我們利用JUnit的before/after生命周期方法:
private Vertx vertx;
private WikiDatabaseService service;
@Before
public void prepare(TestContext context) throws InterruptedException {
vertx = Vertx.vertx();
JsonObject conf = new JsonObject() ①
.put(WikiDatabaseVerticle.CONFIG_WIKIDB_JDBC_URL, "jdbc:hsqldb:mem:testdb;shutdown=true")
.put(WikiDatabaseVerticle.CONFIG_WIKIDB_JDBC_MAX_POOL_SIZE, 4);
vertx.deployVerticle(new WikiDatabaseVerticle(), new DeploymentOptions().setConfig(conf),
context.asyncAssertSuccess(id -> ②
service = WikiDatabaseService.createProxy(vertx,WikiDatabaseVerticle.CONFIG_WIKIDB_QUEUE)));
}
① 我們只是復(fù)寫了Verticle的部分配置勋功,其它使用默認(rèn)值坦报。
② asyncAssertSuccess對(duì)于提供一個(gè)檢測異步操作成功的handler是非常有用的。它有一個(gè)變體是無參的狂鞋,另一個(gè)變體像這個(gè)片择,此時(shí)我們可以鏈接結(jié)果到另一個(gè)handler。
Vert.x的清理很簡單骚揍,我們?cè)俅问褂胊syncAssertSuccess以確保沒有遇到錯(cuò)誤:
@After
public void finish(TestContext context) {
vertx.close(context.asyncAssertSuccess());
}
數(shù)據(jù)庫服務(wù)的操作本質(zhì)上是CRUD操作字管,因此使用一個(gè)JUnit測試用例合并它們?nèi)渴且粋€(gè)測試的好方法:
@Test
public void crud_operations(TestContext context) {
Async async = context.async();
service.createPage("Test","Some content",context.asyncAssertSuccess(v1 -> {
service.fetchPage("Test",context.asyncAssertSuccess(json1 -> {
context.assertTrue(json1.getBoolean("found"));
context.assertTrue(json1.containsKey("id"));
context.assertEquals("Some content",json1.getString("rawContent"));
service.savePage(json1.getInteger("id"),"Yo!",context.asyncAssertSuccess(v2 -> {
service.fetchAllPages(context.asyncAssertSuccess(array1 -> {
context.assertEquals(1,array1.size());
service.fetchPage("Test",context.asyncAssertSuccess(json2 -> {
context.assertEquals("Yo!",json2.getString("rawContent"));
service.deletePage(json1.getInteger("id"),v3 -> {
service.fetchAllPages(context.asyncAssertSuccess(array2 -> {
context.assertTrue(array2.isEmpty());
async.complete();①
}));
});
}));
}));
}));
}));
}));
async.awaitSuccess(5000);②
}
① 這是唯一的Async最終完成的地方。
② 這是在退出測試用例方法和依賴JUnit超時(shí)之間的取舍信不。此處嘲叔,測試用例線程執(zhí)行等待直到Async完成或者超時(shí)周期過去。