<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-unit</artifactId>
<version>5.0.12</version>
<scope>test</scope>
</dependency>
Vert.x Unit
异步多语言单元测试。
简介
Vert.x Unit 旨在通过多语言 API 编写异步单元测试并在 JVM 中运行这些测试。Vert.x Unit API 借鉴了 JUnit 或 QUnit 等现有测试框架,并遵循 Vert.x 的惯例实践。
因此,Vert.x Unit 是测试 Vert.x 应用程序的自然之选。
若要使用 Vert.x Unit,请将以下依赖项添加到构建描述文件的 dependencies 部分:
-
Maven(在你的
pom.xml中):
-
Gradle(在你的
build.gradle文件中):
testCompile ${io.vertx}:${vertx-unit}:5.0.12
Vert.x Unit 可以通过不同方式使用,并且可以运行在你代码运行的任何地方,只需以正确的方式报告结果即可。以下示例展示了一个最简的测试套件:
TestSuite suite = TestSuite.create("the_test_suite");
suite.test("my_test_case", context -> {
String s = "value";
context.assertEquals("value", s);
});
suite.run();
run 方法将执行该套件并遍历套件中的所有测试。套件无论是失败还是通过,如果外界没有感知到测试结果,这都无关紧要。
执行时,测试套件现在会将测试套件的各个步骤报告到控制台:
Begin test suite the_test_suite Begin test my_test Passed my_test End test suite the_test_suite , run: 1, Failures: 0, Errors: 0
reporters 选项用于配置套件运行器在报告测试执行情况时所使用的报告器,详情请参阅 报告 章节。
编写测试套件
测试套件是一组具有名称的测试用例集合,测试用例是一个直接执行的回调。测试套件可以拥有在测试用例或测试套件_之前(before)_和/或_之后(after)_执行的生命周期回调,用于初始化或销毁测试套件所使用的服务。
TestSuite suite = TestSuite.create("the_test_suite");
suite.test("my_test_case_1", context -> {
// 测试 1
});
suite.test("my_test_case_2", context -> {
// 测试 2
});
suite.test("my_test_case_3", context -> {
// 测试 3
});
该 API 是链式调用的(fluent),因此测试用例可以连缀声明:
TestSuite suite = TestSuite.create("the_test_suite");
suite.test("my_test_case_1", context -> {
// 测试 1
}).test("my_test_case_2", context -> {
// 测试 2
}).test("my_test_case_3", context -> {
// 测试 3
});
测试用例的声明顺序不保证执行顺序,因此测试用例不应依赖另一个测试用例的执行来运行。这种做法被视为不良实践。
Vert.x Unit 提供了 before 和 after 回调来进行全局设置(setup)或清理(cleanup):
TestSuite suite = TestSuite.create("the_test_suite");
suite.before(context -> {
// 测试套件设置
}).test("my_test_case_1", context -> {
// 测试 1
}).test("my_test_case_2", context -> {
// 测试 2
}).test("my_test_case_3", context -> {
// 测试 3
}).after(context -> {
// 测试套件清理
});
方法的声明顺序无关紧要,示例中在测试用例之前声明了 before 回调,在测试用例之后声明了 after 回调,但只要在运行测试套件之前完成声明,它们可以出现在任何位置。
before 回调在任何测试之前执行,当其失败时,测试套件的执行将停止并报告失败。after 回调是测试套件执行的最后一个回调,除非 before 回调报告了失败。
同样地,Vert.x Unit 提供了 beforeEach 和 afterEach 回调,它们的功能相同,但会针对每个测试用例分别执行:
TestSuite suite = TestSuite.create("the_test_suite");
suite.beforeEach(context -> {
// 测试用例设置
}).test("my_test_case_1", context -> {
// 测试 1
}).test("my_test_case_2", context -> {
// 测试 2
}).test("my_test_case_3", context -> {
// 测试 3
}).afterEach(context -> {
// 测试用例清理
});
beforeEach 回调在每个测试用例之前执行,当其失败时,该测试用例将不会被执行并报告失败。afterEach 回调紧随测试用例回调之后执行,除非 beforeEach 回调报告了失败。
断言
Vert.x Unit 提供了 TestContext 对象,用于在测试用例中进行断言。该 context 对象提供了处理断言时的常用方法。
assertEquals
断言两个对象相等,适用于_基本_类型或 JSON 类型。
suite.test("my_test_case", context -> {
context.assertEquals(10, callbackCount);
});
还有一个用于提供错误消息的重载版本:
suite.test("my_test_case", context -> {
context.assertEquals(10, callbackCount, "Should have been 10 instead of " + callbackCount);
});
通常每个断言都会提供一个重载版本。
assertNotEquals
assertEquals 的对应反向方法。
suite.test("my_test_case", context -> {
context.assertNotEquals(10, callbackCount);
});
assertNull
断言对象为 null,适用于_基本_类型或 JSON 类型。
suite.test("my_test_case", context -> {
context.assertNull(null);
});
assertNotNull
assertNull 的对应反向方法。
suite.test("my_test_case", context -> {
context.assertNotNull("not null!");
});
assertInRange
assertInRange 针对实数。
suite.test("my_test_case", context -> {
// 断言 0.1 等于 0.2 +/- 0.5
context.assertInRange(0.1, 0.2, 0.5);
});
assertTrue 与 assertFalse
断言布尔表达式的值。
suite.test("my_test_case", context -> {
context.assertTrue(var);
context.assertFalse(value > 10);
});
主动失败
最后但也同样重要的是,test 提供了 fail 方法,该方法将抛出断言错误:
suite.test("my_test_case", context -> {
context.fail("That should never happen");
// 后续语句将不会被执行
});
失败原因既可以是如前所述的_字符串_,也可以是一个_错误(error)。_error 对象取决于目标语言:对于 Java 或 Groovy,它可以是继承自 Throwable 的任何类;对于 JavaScript,它是一个 error;对于 Ruby,它是一个 Exception。
使用第三方断言框架
也可以使用任何其他断言框架,例如流行的 hamcrest 和 assertj。推荐的做法是使用 verify 并在传入的 Handler 内执行断言。通过这种方式,异步测试的终止将得到正确处理。
suite.test("my_test_case", context -> context.verify(v -> {
// 此处使用来自 junit 的 Assert,也可以是 assertj、hamcrest 或任何其他断言库
// 甚至可以手动抛出 AssertionError。
Assert.assertNotNull("not null!");
Assert.assertEquals(10, callbackCount);
}));
异步测试
前面的示例都假设测试用例在其各自的回调之后就终止了,这是测试用例回调的默认行为。但通常需要在测试用例回调之后才终止测试,例如:
suite.test("my_test_case", context -> {
Async async = context.async();
eventBus.consumer("the-address", msg -> {
(2)
async.complete();
});
(1)
});
| 1 | 回调退出,但测试用例并未终止 |
| 2 | 来自 Event Bus 的事件回调终止了测试 |
当未调用 complete 回调时,测试用例将在超时后失败。
|
可以在同一个测试用例中创建多个 Async 对象,必须全部_完成(completed)_才能终止测试。
|
Vert.x 5 使用 JVM 的 由于 如果将客户端(例如 为了避免这种情况,请在异步工作期间保持客户端强引用可达(例如将其存储在字段中),并在适当时首选显式调用 |
suite.test("my_test_case", context -> {
HttpClient client = vertx.createHttpClient();
client.request(HttpMethod.GET, 8080, "localhost", "/").onComplete(context.asyncAssertSuccess(req -> {
req.send().onComplete(context.asyncAssertSuccess(resp -> {
context.assertEquals(200, resp.statusCode());
}));
}));
Async async = context.async();
vertx.eventBus().consumer("the-address", msg -> {
async.complete();
});
});
Async 对象也可以在 before 或 after 回调中使用,在 before 回调中实现依赖于一个或多个异步结果的初始化设置时,这会非常方便:
suite.before(context -> {
Async async = context.async();
HttpServer server = vertx.createHttpServer();
server.requestHandler(requestHandler);
server.listen(8080).onComplete(ar -> {
context.assertTrue(ar.succeeded());
async.complete();
});
});
可以等待特定 Async 完成,类似于 Java 的 CountDownLatch:
Async async = context.async();
HttpServer server = vertx.createHttpServer();
server.requestHandler(requestHandler);
server.listen(8080).onComplete(ar -> {
context.assertTrue(ar.succeeded());
async.complete();
});
// 等待完成
async.awaitSuccess();
| 这不应在 Event Loop 中执行! |
Async 也可以使用初始计数值创建,使用 countDown 使倒计时归零时完成:
Async async = context.async(2);
HttpServer server = vertx.createHttpServer();
server.requestHandler(requestHandler);
server.listen(8080).onComplete(ar -> {
context.assertTrue(ar.succeeded());
async.countDown();
});
vertx.setTimer(1000, id -> {
async.complete();
});
// 等待定时器和 HTTP 请求完成
async.awaitSuccess();
在 async 上调用 complete() 会像往常一样完成该 async,实际上它将计数值直接设为 0。
异步断言
TestContext 提供了有用的方法,为异步测试提供了强大的结构机制:
asyncAssertSuccess 方法返回一个类似于 Async 的 Handler<AsyncResult<T>> 实例,在成功时解析(resolve)该 Async,并在失败时使测试失败并附带失败原因。
Async async = context.async();
vertx.deployVerticle("my.verticle").onComplete(ar -> {
if (ar.succeeded()) {
async.complete();
} else {
context.fail(ar.cause());
}
});
// 可以替换为
vertx.deployVerticle("my.verticle").onComplete(context.asyncAssertSuccess());
asyncAssertSuccess 方法返回一个类似于 Async 的 Handler<AsyncResult<T>> 实例,在成功时调用委托的 Handler<T>,并在失败时使测试失败并附带失败原因。
AtomicBoolean started = new AtomicBoolean();
Async async = context.async();
vertx.deployVerticle(new AbstractVerticle() {
public void start() throws Exception {
started.set(true);
}
}).onComplete(ar -> {
if (ar.succeeded()) {
context.assertTrue(started.get());
async.complete();
} else {
context.fail(ar.cause());
}
});
// 可以替换为
vertx.deployVerticle("my.verticle").onComplete(context.asyncAssertSuccess(id -> {
context.assertTrue(started.get());
}));
当 Handler 退出时 async 即告完成,除非在调用期间创建了新的 async,这对于_链式连缀_异步行为非常方便:
Async async = context.async();
vertx.deployVerticle("my.verticle").onComplete(ar1 -> {
if (ar1.succeeded()) {
vertx.deployVerticle("my.otherverticle").onComplete(ar2 -> {
if (ar2.succeeded()) {
async.complete();
} else {
context.fail(ar2.cause());
}
});
} else {
context.fail(ar1.cause());
}
});
// 可以替换为
vertx.deployVerticle("my.verticle").onComplete(context.asyncAssertSuccess(id ->
vertx.deployVerticle("my_otherverticle").onComplete(context.asyncAssertSuccess())
));
asyncAssertFailure 方法返回一个类似于 Async 的 Handler<AsyncResult<T>> 实例,在失败时解析该 Async,并在成功时使测试失败。
Async async = context.async();
vertx.deployVerticle("my.verticle").onComplete(ar -> {
if (ar.succeeded()) {
context.fail();
} else {
async.complete();
}
});
// 可以替换为
vertx.deployVerticle("my.verticle").onComplete(context.asyncAssertFailure());
asyncAssertFailure 方法返回一个类似于 Async 的 Handler<AsyncResult<T>> 实例,在失败时调用委托的 Handler<Throwable>,并在成功时使测试失败。
Async async = context.async();
vertx.deployVerticle("my.verticle").onComplete(ar -> {
if (ar.succeeded()) {
context.fail();
} else {
context.assertTrue(ar.cause() instanceof IllegalArgumentException);
async.complete();
}
});
// 可以替换为
vertx.deployVerticle("my.verticle").onComplete(context.asyncAssertFailure(cause -> {
context.assertTrue(cause instanceof IllegalArgumentException);
}));
当 Handler 退出时 async 即告完成,除非在调用期间创建了新的 async。
重复测试
当测试随机失败或不常失败时(例如竞态条件),多次运行同一测试以增加测试失败的可能性会很方便。
TestSuite.create("my_suite").test("my_test", 1000, context -> {
// 这将执行 1000 次
});
若声明了 beforeEach 和 afterEach 回调,它们将随测试执行次数相应地执行多次。
| 测试重复执行是串行的 |
共享对象
TestContext 具有用于在回调之间共享状态的 get/put/remove 操作。
在 before 回调期间添加的任何对象在任何其他回调中均可用。每个测试用例都将在共享状态的副本上运行,因此更新仅对当前测试用例可见。
TestSuite.create("my_suite").before(context -> {
// host 对所有测试用例可用
context.put("host", "localhost");
}).beforeEach(context -> {
// 为每个测试生成一个随机端口
int port = helper.randomPort();
// 获取 host
String host = context.get("host");
// 设置服务器
Async async = context.async();
HttpServer server = vertx.createHttpServer();
server.requestHandler(req -> {
req.response().setStatusCode(200).end();
});
server.listen(port, host).onComplete(ar -> {
context.assertTrue(ar.succeeded());
context.put("port", port);
async.complete();
});
}).test("my_test", context -> {
// 获取共享状态
int port = context.get("port");
String host = context.get("host");
// 发起请求
HttpClient client = vertx.createHttpClient();
client.request(HttpMethod.GET, port, host, "/resource").onComplete(context.asyncAssertSuccess(req -> {
req.send().onComplete(context.asyncAssertSuccess(resp -> {
context.assertEquals(200, resp.statusCode());
}));
}));
});
| 共享任意对象仅在 Java 中受支持,其他语言只能共享基本类型或 JSON 类型。其他对象应使用该语言自身的特性来共享。 |
运行
创建测试套件后,在调用 run 方法之前它不会被执行。
suite.run();
也可以使用指定的 Vertx 实例来运行测试套件:
suite.run(vertx);
当使用 Vertx 实例运行时,测试套件将使用 Vert.x Event Loop 执行,详情请参阅 Event Loop 章节。
| 可以在同一个 Verticle 中执行多个测试套件,Vert.x Unit 会等待所有执行的套件完成。 |
测试套件完成
无法预先假设测试套件何时完成,如果某些代码需要在测试套件之后执行,它应该放在测试套件的 after 回调中,或者作为 Completion 的回调:
TestCompletion completion = suite.run(vertx);
// 简单的完成回调
completion.handler(ar -> {
if (ar.succeeded()) {
System.out.println("Test suite passed!");
} else {
System.out.println("Test suite failed:");
ar.cause().printStackTrace();
}
});
Completion 对象还提供了一个接收 Promise 对象的 resolve 方法,该 Promise 将接收测试套件执行结果的通知:
TestCompletion completion = suite.run();
// 当套件完成时,promise 将被 resolve
completion.resolve(startPromise);
这样可以轻松创建一个_测试_ Verticle,其部署操作就是测试套件的执行,从而使部署它的代码能够轻松感知成功或失败。
Completion 对象还可以像闭锁一样使用,以阻塞直至测试套件完成。当运行测试套件的线程与当前线程不同时,应使用此方式:
Completion completion = suite.run();
// 等待测试套件完成
completion.await();
当线程被中断或触发超时时,await 会抛出异常。
awaitSuccess 是一个变体,当测试套件失败时会抛出异常。
Completion completion = suite.run();
// 等待测试套件成功,否则抛出异常
completion.awaitSuccess();
超时
测试套件中的每个测试用例都必须在达到指定超时时间之前执行完毕。默认超时时间为 2 分钟,可以通过_测试选项(test options)_进行更改:
TestOptions options = new TestOptions().setTimeout(10000);
// 以 10 秒超时时间运行
suite.run(options);
Event Loop
Vert.x Unit 的执行是一个待执行的任务列表,每个任务的执行都由前一个任务的完成驱动。这些任务应尽可能利用 Vert.x Event Loop,但这取决于当前的执行上下文(即测试套件是在 main 中执行还是嵌入在 Verticle 中)以及是否配置了 Vertx 实例。
setUseEventLoop 用于配置 Event Loop 的使用方式:
| useEventLoop:null | useEventLoop:true | useEventLoop:false | |
|---|---|---|---|
|
使用 Vert.x Event Loop |
使用 Vert.x Event Loop |
强制不使用 Event Loop |
在 |
使用当前 Event Loop |
使用当前 Event Loop |
强制不使用 Event Loop |
在 main 中 |
不使用 Event Loop |
抛出错误 |
不使用 Event Loop |
默认的 useEventLoop 值为 null,这意味着它会在可能的情况下使用 Event Loop,并在没有可用 Event Loop 时回退到不使用 Event Loop。
报告
报告是测试套件的重要组成部分,Vert.x Unit 可以配置为与不同种类的报告器配合运行。
默认情况下未配置任何报告器,运行测试套件时,可以提供_测试选项(test options)_来配置一个或多个报告器:
ReportOptions consoleReport = new ReportOptions().
setTo("console");
// 将 JUnit 文件报告输出到当前目录
ReportOptions junitReport = new ReportOptions().
setTo("file:.").
setFormat("junit");
suite.run(new TestOptions().
addReporter(consoleReport).
addReporter(junitReport)
);
文件报告
报告输出至文件,必须提供 Vertx 实例:
- to
-
file
:目录名 - format
-
simple 或 junit
- example
-
file:.
文件报告器将在配置的目录中创建文件,文件将根据所执行的测试套件名称和格式命名(即 simple 创建 txt 文件,junit 创建 xml 文件)。
Event Bus 报告
将事件报告到 Event Bus,必须提供 Vertx 实例:
- to
-
bus
:Event Bus 地址 - example
-
bus:the-address
这可以将测试套件的执行与报告解耦。
通过 Event Bus 发送的消息可以由 EventBusCollector 收集并实现自定义报告:
EventBusCollector collector = EventBusCollector.create(
vertx,
new ReportingOptions().addReporter(
new ReportOptions().setTo("file:report.xml").setFormat("junit")));
collector.register("the-address");
Vert.x 集成
默认情况下,断言和失败处理必须在 TestContext 上完成,并且只有在由 Vert.x Unit 调用时抛出断言错误才有效:
suite.test("my_test_case", ctx -> {
// 该失败将由 Vert.x Unit 报告
throw new RuntimeException("it failed!");
});
在常规 Vert.x 回调中,该失败将被忽略:
suite.test("test-server", testContext -> {
HttpServer server = vertx.createHttpServer().requestHandler(req -> {
if (req.path().equals("/somepath")) {
throw new AssertionError("Wrong path!");
}
req.response().end();
});
});
自 Vert.x 3.3 起,可以设置全局异常处理器以报告 Event Loop 中未捕获的异常:
suite.before(testContext -> {
// 将未捕获的异常作为 Vert.x Unit 失败进行报告
vertx.exceptionHandler(testContext.exceptionHandler());
});
suite.test("test-server", testContext -> {
HttpServer server = vertx.createHttpServer().requestHandler(req -> {
if (req.path().equals("/somepath")) {
throw new AssertionError("Wrong path!");
}
req.response().end();
});
});
异常处理器是在 before 阶段设置的,TestContext 在各个 before、test 和 after 阶段之间共享。因此在 before 阶段获取的异常处理器是正确的。
JUnit 集成
尽管 Vert.x Unit 是多语言的且并不基于 JUnit,但仍可以从 JUnit 中运行 Vert.x Unit 测试套件或测试用例,从而使你能够将测试与 JUnit 以及构建系统或 IDE 集成。
@RunWith(VertxUnitRunner.class)
public class JUnitTestSuite {
@Test
public void testSomething(TestContext context) {
context.assertFalse(false);
}
}
VertxUnitRunner 使用 JUnit 注解来自省类,并在该类之后创建测试套件。测试方法应声明一个 TestContext 参数,如果不声明也可以。然而,TestContext 是检索关联的 Vertx 实例或执行异步测试的唯一途径。
JUnit 集成也可用于 Groovy 语言,使用 io.vertx.groovy.ext.unit.junit.VertxUnitRunner 运行器。
在 Vert.x 上下文中运行测试
默认情况下,调用测试方法的线程是 JUnit 线程。RunTestOnContext JUnit 规则可用于更改此行为,以便使用 Vert.x Event Loop 线程运行这些测试方法。
因此,当在测试方法与 Vert.x 处理器(handler)之间共享状态时必须谨慎,因为它们不在同一个线程上,例如在 Vert.x 处理器中递增计数器并在测试方法中断言该计数器。解决此问题的一种方法是使用适当的同步,另一种方法是在 Vert.x 上下文上执行测试方法,该上下文将传播到所创建的处理器中。
为此,RunTestOnContext 规则需要一个 Vertx 实例。可以显式提供该实例,否则规则将在底层自行管理一个实例。该实例可以在测试运行时进行检索,这也使得该规则成为管理 Vertx 实例的一种方式。
@RunWith(VertxUnitRunner.class)
public class RunOnContextJUnitTestSuite {
@Rule
public RunTestOnContext rule = new RunTestOnContext();
@Test
public void testSomething(TestContext context) {
// 使用底层的 vertx 实例
Vertx vertx = rule.vertx();
}
}
该规则可以使用 @Rule 或 @ClassRule 进行注解,前者为每个测试管理一个 Vert.x 实例,后者为该类的测试方法管理单个 Vert.x 实例。
请牢记,使用此规则时切勿阻塞 Event Loop。使用 CountDownLatch 等类似类时必须十分谨慎。
|
超时
Vert.x Unit 的 2 分钟超时时间可以通过 @Test 注解的 timeout 属性进行覆盖:
public class JunitTestWithTimeout {
@Test(timeout = 1000l)
public void testSomething(TestContext context) {
//...
}
}
对于更全局的配置,可以使用 Timeout 规则:
@RunWith(VertxUnitRunner.class)
public class TimeoutTestSuite {
@Rule
public Timeout rule = Timeout.seconds(1);
@Test
public void testSomething(TestContext context) {
//...
}
}
请使用 io.vertx.ext.unit.junit.Timeout,而非 org.junit.rules.Timeout。
|
方法级别的超时会覆盖类级别的超时:
@RunWith(VertxUnitRunner.class)
public class TimeoutOverwriteTestSuite {
@Rule
public Timeout rule = Timeout.millis(5_000L);
@Test
public void testQuick(TestContext context) {
//...
}
@Test(timeout = 30_000L)
public void testSlow(TestContext context) {
//...
}
}
@Test 的超时时间会覆盖 io.vertx.ext.unit.junit.Timeout 规则,但不会覆盖 org.junit.rules.Timeout 规则。
|
参数化测试
JUnit 提供了实用的 Parameterized 测试,得益于 VertxUnitRunnerWithParametersFactory,Vert.x Unit 测试可以使用该专用运行器来运行:
@RunWith(Parameterized.class)
@Parameterized.UseParametersRunnerFactory(VertxUnitRunnerWithParametersFactory.class)
public class SimpleParameterizedTest {
@Parameterized.Parameters
public static Iterable<Integer> data() {
return Arrays.asList(0, 1, 2);
}
public SimpleParameterizedTest(int value) {
//...
}
@Test
public void testSomething(TestContext context) {
// 使用当前值执行测试
}
}
参数化测试也可以在 Groovy 中通过 io.vertx.groovy.ext.unit.junit.VertxUnitRunnerWithParametersFactory 来完成。
重复测试
当测试随机失败或不常失败时(例如竞态条件),多次运行同一测试以增加测试失败的可能性会很方便。
在 JUnit 中,测试必须使用 @Repeat 进行注解才能重复执行。测试还必须在其规则中定义 RepeatRule。
@RunWith(VertxUnitRunner.class)
public class RepeatingTest {
@Rule
public RepeatRule rule = new RepeatRule();
@Repeat(1000)
@Test
public void testSomething(TestContext context) {
// 这将执行 1000 次
}
}
声明后,before 和 after 生命周期将随测试执行次数相应地执行多次。
| 测试重复执行是串行的 |
与其他断言库结合使用
Vert.x Unit 的易用性在 Vert.x 3.3 中得到了极大改进。你现在可以使用 Hamcrest、http://joel-costigliola.github.io/assertj/[AssertJ]、https://github.com/rest-assured/rest-assured/[Rest Assured] 或任何你想要的断言库来编写测试。这得益于 Vert.x 集成 中描述的全局异常处理器。
你可以在 vertx-examples 项目中找到将 Vert.x Unit 与 Hamcrest 和 AssertJ 结合使用的 Java 示例。
Java 语言集成
测试套件集成
Java 语言提供了类,并且可以根据以下映射规则直接从 Java 类创建测试套件:
对 testSuiteObject 参数的方法进行自省,保留带有 TestContext 参数的 public、非 static 方法,并通过方法名映射到 Vert.x Unit 测试套件:
-
before:before 回调 -
after:after 回调 -
beforeEach:beforeEach 回调 -
afterEach:afterEach 回调 -
当名称以 test 开头时:以方法名命名的测试用例回调
public class MyTestSuite {
public void testSomething(TestContext context) {
context.assertFalse(false);
}
}
这个类可以轻松转换为 Vert.x 测试套件:
TestSuite suite = TestSuite.create(new MyTestSuite());