Examples of tests written using following approach can be found here
...
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();
...
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, database client should be configured.
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);
}
...
- create file with configuration:
{
"host" : "localhost",
"port" : 5432,
"database" : "okapi_modules",
"username" : "folio_admin",
"password" : "folio_admin"
} - set folio.password.validator.test.database property to 'external';
- put path to config file in property org.folio.password.validator.test.config;
...
-Dorg.folio.password.validator.test.config=/postgres-conf.json
Running
...
tests via maven:
Example (file with name "postgres-conf.json" is located in resources root):
...
Running tests in IDE (intelliji idea example):
Mock HTTP calls:
WireMock framework allows to create and setup /* Define wiremock test dependency */
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.19.0</version>
<scope>test</scope>
</dependency>
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 use userMockServer#port
Notifier logs all incoming call calls to mock server.
@org.junit.Rule
public WireMockRule userMockServer = new WireMockRule(
WireMockConfiguration.wireMockConfig()
.dynamicPort()
.notifier(new ConsoleNotifier(true)));
...
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 for <request matching>:
WireMock has advanced features for request matching (withHeader, withCookie, withQueryParam, withRequestBody) :
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 <request matching> variants can be found in documentation Response TemplatingRequest Matching
Examples for <response definition> :
WireMock.aResponse()
.withStatus(200)
.withHeader("response-header", "value")
.withBody(someByteArray);
WireMock.ok("bodyHere")
WireMock.okJson(someJsonResponse)
WireMock.notFound()
All <response definition> variants can be found in documentation Response Templating
Perform REST API testing:
Rest-Assured provides a fluent API to create readable tests for REST resources:
...
Required dependencies:
/* Exactly that rest-assured library */
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>${rest-assured.version}</version>
<scope>test</scope>
</dependency>
/* By default RestAssured uses jackson databinding under the hoods, if such functionality doesn't exist - you can use fasterxml.jackson.core */
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.3</version>
</dependency>
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 hereRestAssured use cases are described is this guide: Testing RESTful Services in Java: Best Practices
Unit testing:
This section does not describe unit testing approach in general but only some confusing cases about testing asynchronous calls.
...
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;
}
}
Example of test:
@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);
}
}
...