Examples of tests written using following approach can be found here
Testing REST API:
Most of okapi back-end modules are covered with API tests using Vertx unit
Test setup:
Add runner:
@RunWith(VertxUnitRunner.class)
Run application (deploy verticle):
During test setup a verticle should be deployed to vertx instance (custom verticle class or RestVerticle class in case of using raml module builder)
private static Vertx vertx;
private static int port;
@BeforeClass
public static void setUpClass(final TestContext context) throws Exception {
Async async = context.async();
vertx = Vertx.vertx();
port = NetworkUtils.nextFreePort();
//database setup goes here if necessary
TenantClient tenantClient = new TenantClient("localhost", port, "diku", "dummy-token");
DeploymentOptions restVerticleDeploymentOptions = new DeploymentOptions().setConfig(new JsonObject().put("http.port", port));
vertx.deployVerticle(RestVerticle.class.getName(), restVerticleDeploymentOptions, res -> {
try {
tenantClient.post(null, res2 -> {
async.complete();
});
} catch (Exception e) {
e.printStackTrace();
}
});
}
Run database:
If test covers database interaction, a database instance should be run.
Following code provides a possibility to use existing database for tests or run database in embedded mode (slower).
...
Mock HTTP calls:
WireMock framework allows to create and setup mock server:
Following code runs new mock server for every test on dynamic port.
To get port value userMockServer#port
Notifier logs all incoming call to mock server.
@org.junit.Rule
public WireMockRule userMockServer = new WireMockRule(
WireMockConfiguration.wireMockConfig()
.dynamicPort()
.notifier(new ConsoleNotifier(true)));
To mock response to specific request use template:
WireMock.stubFor(<request matching>.willReturn(<response definition>));
Example:
WireMock.stubFor(WireMock.get("/users?query=id==" + ADMIN_ID)
.willReturn(WireMock.okJson(someJsonResponse)
));
All <request matching> variants can be found in documentation Request Matching
Examples:
WireMock.post(WireMock.urlMatching("regexp here"))
.withRequestBody(WireMock.equalToJson("json here"))
WireMock.any(WireMock.urlMatching("regexp here"))
.withHeader("header", WireMock.equalTo("value"))
.withHeader("header2", WireMock.absent())
.withHeader("header3", WireMock.matching("regexp"))
.withQueryParam("param", WireMock.containing("substring))
All <response definition> variants can be found in documentation Response Templating
Examples:
WireMock.aResponse()
.withStatus(200)
.withHeader("response-header", "value")
.withBody(someByteArray);
WireMock.ok("bodyHere")
WireMock.okJson(someJsonResponse)
WireMock.notFound()
Perform REST API testing:
Rest-Assured provides a fluent API to create readable tests for REST resources:
RestAssured.given()
.param("limit", 20)
.when()
.get("blogs")
.then()
.statusCode(200);
One of the ways to use RestAssured is following:
RestAssured.given()
<building request>
.when()
<specifying method and path>
.then()
<response verification>
In <building request> section it is possible to specify headers, params, body of request.
In <specifying method and path> goes method and path specification.
In <response verification> result is verified.
A lot of best practices and use cases for testing REST API using RestAssured are described here
Unit testing:
This section does not describe unit testing approach in general but only some confusing cases about testing asynchronous calls.
Waiting and verifying async results:
Testing methods wtih Handler<AsyncResult> parameter:
@RunWith(VertxUnitRunner.class)
...
@Test
public void test(TestContext testContext) {
//some test setup
//expect
Handler<AsyncResult<JsonObject>> checkingHandler = testContext.asyncAssertSuccess(response -> {
//asserting goes here
Assert.assertThat(actual, Matchers.is(expected));
Mockito.verify(...);
});
someService.someAction(params, checkingHandler);
}
TestContext parameter should be added to test method.
To expect failed async result use testContext#asyncAssertFailureExamples of tests written using following approach can be found here
Testing REST API:
Most of okapi back-end modules are covered with API tests using Vertx unit
Test setup:
Add runner:
@RunWith(VertxUnitRunner.class)
Run application (deploy verticle):
During test setup a verticle should be deployed to vertx instance (custom verticle class or RestVerticle class in case of using raml module builder)
private static Vertx vertx;
private static int port;
@BeforeClass
public static void setUpClass(final TestContext context) throws Exception {
Async async = context.async();
vertx = Vertx.vertx();
port = NetworkUtils.nextFreePort();
//database setup goes here if necessary
TenantClient tenantClient = new TenantClient("localhost", port, "diku", "dummy-token");
DeploymentOptions restVerticleDeploymentOptions = new DeploymentOptions().setConfig(new JsonObject().put("http.port", port));
vertx.deployVerticle(RestVerticle.class.getName(), restVerticleDeploymentOptions, res -> {
try {
tenantClient.post(null, res2 -> {
async.complete();
});
} catch (Exception e) {
e.printStackTrace();
}
});
}
Run database:
If test covers database interaction, a database instance should be run.
Following code provides a possibility to use existing database for tests or run database in embedded mode (slower).
useExternalDatabase = System.getProperty(
"org.folio.password.validator.test.database",
"embedded");
switch (useExternalDatabase) {
case "environment":
System.out.println("Using environment settings");
break;
case "external":
String postgresConfigPath = System.getProperty(
"org.folio.password.validator.test.config",
"/postgres-conf-local.json");
PostgresClient.setConfigFilePath(postgresConfigPath);
break;
case "embedded":
PostgresClient.setIsEmbedded(true);
PostgresClient.getInstance(vertx).startEmbeddedPostgres();
break;
default:
String message = "No understood database choice made." +
"Please set org.folio.password.validator.test.database" +
"to 'external', 'environment' or 'embedded'";
throw new Exception(message);
}
Mock HTTP calls:
WireMock framework allows to create and setup mock server:
Following code runs new mock server for every test on dynamic port.
To get port value userMockServer#port
Notifier logs all incoming call to mock server.
@org.junit.Rule
public WireMockRule userMockServer = new WireMockRule(
WireMockConfiguration.wireMockConfig()
.dynamicPort()
.notifier(new ConsoleNotifier(true)));
To mock response to specific request use template:
WireMock.stubFor(<request matching>.willReturn(<response definition>));
Example:
WireMock.stubFor(WireMock.get("/users?query=id==" + ADMIN_ID)
.willReturn(WireMock.okJson(someJsonResponse)
));
All <request matching> variants can be found in documentation Request Matching
Examples:
WireMock.post(WireMock.urlMatching("regexp here"))
.withRequestBody(WireMock.equalToJson("json here"))
WireMock.any(WireMock.urlMatching("regexp here"))
.withHeader("header", WireMock.equalTo("value"))
.withHeader("header2", WireMock.absent())
.withHeader("header3", WireMock.matching("regexp"))
.withQueryParam("param", WireMock.containing("substring))
All <response definition> variants can be found in documentation Response Templating
Examples:
WireMock.aResponse()
.withStatus(200)
.withHeader("response-header", "value")
.withBody(someByteArray);
WireMock.ok("bodyHere")
WireMock.okJson(someJsonResponse)
WireMock.notFound()
Perform REST API testing:
Rest-Assured provides a fluent API to create readable tests for REST resources:
RestAssured.given()
.param("limit", 20)
.when()
.get("blogs")
.then()
.statusCode(200);
One of the ways to use RestAssured is following:
RestAssured.given()
<building request>
.when()
<specifying method and path>
.then()
<response verification>
In <building request> section it is possible to specify headers, params, body of request.
In <specifying method and path> goes method and path specification.
In <response verification> result is verified.
A lot of best practices and use cases for testing REST API using RestAssured are described here
Unit testing:
This section does not describe unit testing approach in general but only some confusing cases about testing asynchronous calls.
Waiting and verifying asynchronous results:
Testing methods wtih Handler<AsyncResult> parameter:
@RunWith(VertxUnitRunner.class)
...
@Test
public void test(TestContext testContext) {
//some test setup
//expect
Handler<AsyncResult<JsonObject>> checkingHandler = testContext.asyncAssertSuccess(response -> {
//asserting goes here
Assert.assertThat(actual, Matchers.is(expected));
Mockito.verify(...);
});
someService.someAction(params, checkingHandler);
}
TestContext parameter should be added to test method.
To expect failed asynchronous result use testContext#asyncAssertFailure
Mocking asynchronous method calls:
public interface DummyService {
boolean dummyAsyncCall(String someParameter, Handler<AsyncResult<String>> handler);
}
public class GenericHandlerAnswer<H, R> implements Answer<R> {
private H handlerResult;
private int argumentIndex;
private R returnResult;
public GenericHandlerAnswer(H handlerResult, int handlerArgumentIndex) {
this.handlerResult = handlerResult;
this.argumentIndex = handlerArgumentIndex;
}
public GenericHandlerAnswer(H handlerResult, int handlerArgumentIndex, R returnResult) {
this(handlerResult, handlerArgumentIndex);
this.returnResult = returnResult;
}
@Override
public R answer(InvocationOnMock invocation) {
Handler<H> handler = invocation.getArgument(argumentIndex);
handler.handle(handlerResult);
return returnResult;
}
}
@RunWith(VertxUnitRunner.class)
public class MockDummyService {
@Mock
public DummyService dummyServiceMock;
@Before
public void setUp(TestContext testContext) throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
public void test(TestContext testContext) {
AsyncResult<String> dummyCallResult = Future.succeededFuture("Result for handler");
int handlerParameterIndex = 1;
boolean methodReturnedValue = true;
GenericHandlerAnswer<AsyncResult<String>, Boolean> answer =
new GenericHandlerAnswer<>(dummyCallResult, handlerParameterIndex, methodReturnedValue);
Mockito.doAnswer(answer)
.when(dummyServiceMock)
.dummyAsyncCall(ArgumentMatchers.anyString(), ArgumentMatchers.any());
Handler<AsyncResult<String>> checkingHandler = testContext.asyncAssertSuccess(response -> {
Assert.assertThat(response, Matchers.is(dummyCallResult.result()));
});
boolean result = dummyServiceMock.dummyAsyncCall("some string", checkingHandler);
Assert.assertTrue(result);
}
}