Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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).

...

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, 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);
}

To configure database connection via environment variables:

  • set folio.password.validator.test.database property to 'environment';

  • set following environment variables:

    namevalue (example)
    db.hostlocalhost
    db.port5432
    db.databaseokapi_modules

    db.username

    folio_admin
    db.passwordfolio_admin


To configure database connection via file:

  • 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;

Running testing via maven:

Example (file with name "postgres-conf.json" is located in resources root):

mvn clean install -Dorg.folio.password.validator.test.database=external -Dorg.folio.password.validator.test.config=/postgres-conf.json

Running test in IDE (intelliji idea example):


Image Added

Mock HTTP calls:

WireMock framework allows to create and setup 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

...

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))

...

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:

...

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);
}
}

Useful links: