I tried to write tests with Vert.x 3 Web-Client. It is callback hell. You can read this post.
Now Vert.x 4 has been released. And it contains methods
that return Future
. Future
can
be converted to CompletableFuture
. So I can
block thread with method get()
. It is what I
need for my tests.
My code now looks like this
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
class AccountTest {
private static String token;
private static WebClient webClient;
@BeforeAll
static void beforeAll() throws Throwable {
Vertx vertx = Vertx.vertx();
webClient = WebClient.create(vertx);
login();
}
@Test
public void createAccountTest() throws Exception {
JsonObject account = new JsonObject();
account.put("name", "accountLOSS1");
account.put("type", "LOSS");
account.put("summ", 1000.00);
Future<HttpResponse<Buffer>> responseFuture = webClient
.post(8080, "localhost", "/api/accounts")
.putHeader("Authorization", "Bearer " + token)
.sendJsonObject(account);
HttpResponse<Buffer> response = succeeding(responseFuture);
assertEquals(response.getHeader("content-type"), "application/json");
long accountId = Long.parseLong(response.bodyAsString());
assertTrue(accountId > 0, "No account Id");
}
private static void login() throws Exception {
JsonObject user = new JsonObject();
user.put("login", "testLogin");
user.put("password", "testPassword");
Future<HttpResponse<Buffer>> future = webClient
.post(8080, "localhost", "/api/login")
.sendJsonObject(user);
HttpResponse<Buffer> response = succeeding(future);
token = response.getHeader("X-Auth-Token");
JsonObject userObject = response.bodyAsJsonObject();
assertEquals(login, userObject.getString("name"));
}
private static HttpResponse<Buffer> succeeding(Future<HttpResponse<Buffer>> ar)
throws Exception {
CompletionStage<HttpResponse<Buffer>> completionStage = ar.toCompletionStage();
CompletableFuture<HttpResponse<Buffer>> completableFuture = completionStage.toCompletableFuture();
HttpResponse<Buffer> response = completableFuture.get(10, TimeUnit.SECONDS);
if (response.statusCode() != 200) {
String errors = response.bodyAsString();
Exception exception = new Exception("Wrong http status code: "
+ response.statusCode() + " " + errors);
throw exception;
}
return response;
}
}
Looks good. Looks sync!
I can create a batch of accounts asynchronously too. With
Future
I don't need
VertxTestContext
and checkpoints.
CompletableFuture.allOf
waits for all
requests.
@Test
public void createAccounts() throws Exception {
List<JsonObject> accounts = new ArrayList<>();
Future<HttpResponse<Buffer>> futureOwn1;
{
JsonObject account = new JsonObject();
account.put("name", "accountOWN1");
account.put("type", "OWN");
account.put("summ", 1000.00);
futureOwn1 = webClient.post(8080, "localhost", "/api/accounts")
.putHeader("Authorization", "Bearer " + token)
.sendJsonObject(account);
futureOwn1.compose(response -> {
if (response.statusCode() != 200) {
String errors = response.bodyAsString();
return Future.failedFuture(errors);
}
assertEquals(response.getHeader("content-type"), "application/json");
long accountId = Long.parseLong(response.bodyAsString());
account.put("id", accountId);
return Future.succeededFuture(account);
});
accounts.add(account);
}
Future<HttpResponse<Buffer>> futureOwn2;
{
JsonObject account = new JsonObject();
account.put("name", "accountOWN2");
account.put("type", "OWN");
account.put("summ", 1000.00);
futureOwn2 = webClient.post(8080, "localhost", "/api/accounts")
.putHeader("Authorization", "Bearer " + token)
.sendJsonObject(account);
futureOwn2.compose(response -> {
if (response.statusCode() != 200) {
String errors = response.bodyAsString();
return Future.failedFuture(errors);
}
assertEquals(response.getHeader("content-type"), "application/json");
long accountId = Long.parseLong(response.bodyAsString());
account.put("id", accountId);
return Future.succeededFuture(account);
});
accounts.add(account);
}
CompletableFuture.allOf(
futureOwn1.toCompletionStage().toCompletableFuture(),
futureOwn2.toCompletionStage().toCompletableFuture(),
).get(10, TimeUnit.SECONDS);
List<JsonObject> fromDb = getAccounts();
for (JsonObject account : accounts) {
boolean found = fromDb.stream()
.anyMatch(db -> db.getLong("id").equals(account.getLong("id")));
Assertions.assertTrue(found, "Not found in DB " + account);
}
}
Conclusion
With Vert.x 4 I can make my code more clean and readable.