Testing Knot.x with JUnit 5
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
.github
gradle/wrapper
src/main
.editorconfig
.gitignore
.travis.yml
CHANGELOG.md
CODE_OF_CONDUCT.md
CONTRIBUTING.md
LICENSE
README.md
RELEASING.md
build.gradle
gradle.properties
gradlew
gradlew.bat
release.gradle
settings.gradle

README.md

Knot.x JUnit 5

JUnit 5 extensions and data type converters for Knot.x integration tests. Those tests allow to setup Knot.x instance with declared modules. It can be used both for module tests and regression tests.

Extensions

Provides following helpers:

KnotxExtension

Knot.x-specific extension that manages test Vert.x instances (with Knot.x configuration injection) and WireMock servers (through KnotxWiremockExtension).

Example usage:

@ExtendWith(KnotxExtension.class)
public class ExampleIntegrationTest {

  @KnotxWiremock(port = 4001)
  protected WireMockServer mockRepository;

  @Test
  @KnotxApplyConfiguration({"default.conf", "overloaded.conf"})
  public void callModule_validKnotContextResult(VertxTestContext context, Vertx vertx) {
    // ...
  }
}

See Vert.x JUnit 5 integration for guide how to interact with VertxTestContext instances. Also, under io.knotx.junit5.util.RequestUtil there are some common methods that should make working with Vert.x tests contexts easier.

@KnotxApplyConfiguration

The annotation allows to specify one and more Knot.x configuration(s) to load for given test. It accepts a paths array and loads all configuration entries using Vert.x Config file stores, through a custom implementation enhanced with support for HOCON hierarchical configuration loading and cross-file variable references (for more details see section KnotxConcatConfigProcessor down below). It supports two configuration semantics:

  • JSON (files with json extension)
  • HOCON (files with conf extension)

The order of the configuration files is important, as it defines the overloading order (base file first, overrides last). For conflicting (identical) keys in configuration, configuration entries arriving last will overwrite the value provided by the previous configuration files.

See Vert.x Config overloading rules for more details.

KnotxConcatConfigProcessor

This implementation of Vert.x ConfigProcessor enables KnotxExtension to load Knot.x configuration files hierarchically, including cross-file references to variables/placeholders (only with HOCON files).

Vert.x Config limitations

The HOCON cross-references are not available out-of-box in Vert.x Config mechanism. Knot.x supports JSON and HOCON configurations only, so that functionality would be useful in, for example, integration tests that can run in parrallel (i.e. randomization of ports, references to other parts of configuration).

However, Vert.x loads all given files (stores), evaluates them independently and then stiches all files together to create a final result (JSON).

Cross-references example

We have two stores definitions:

"stores": [
      {
        "type": "file",
        "format": "hocon",
        "config": { "path": "config/application.conf" }
      },
      {
        "type": "json",
        "config": { "test.wiremock.mockService.port": 4321 }
      }
    ]

The application.conf file contains

config.somemodule.options.config {
  clientOptions {
    port = ${test.wiremock.mockService.port}
  }
}
test.wiremock {
  mockService {
    port = 1234
  }
}

The default Vert.x Config processing result:

{
  "config": {
    "somemodule": {
      "options": {
        "config": {
          "clientOptions": {
            "port": 1234
          }
        }
      }
    }
  },
  "test": {
    "wiremock": {
      "mockService": {
        "port": 4321
      }
    }
  }
}

Expected behaviour:

{
  "config": {
    "somemodule": {
      "options": {
        "config": {
          "clientOptions": {
            "port": 4321
          }
        }
      }
    }
  },
  "test": {
    "wiremock": {
      "mockService": {
        "port": 4321
      }
    }
  }
}
Solution

KnotxConcatConfigProcessor works around this problem. It creates a new config format, in which you specify a JSON file:

{ 
  "paths": [ "base.conf", "specific.json" ], 
  "overrides": {"baseKey": "newValue"}
}

Files from paths are loaded in given order, and overrides (JSON object) are applied directly on top of resulting HOCON Config object, and only then the configuration gets resolved and effectively returned for Knot.x for processing.

Please refer to HOCON readme if you have any more questions regarding config loading behavior.

KnotxWiremockExtension

Standalone WireMockServer injection and lifecycle management. Allows for:

  • Specifying on which port WireMockServer instance should be present,
  • If no port is specified on annotation, a random one will be assigned to given instance,
  • Running multiple mocked server instances,
  • Referencing mocked servers' port numbers in Knot.x configuration (more details below).

Warning: if you use KnotxExtension, you must not inject KnotxWiremockExtension, as the functionality of the latter gets auto-imported into the former.

WireMockServer injection and naming

WireMockServer instances are recognized by their identifier - either test class instance variable name or test method parameter name.

For example below, two servers will be available for testWiremockRunningOnPort method: mockServiceRandom with randomly assigned port, and server on port 3000.

@ExtendWith(KnotxWiremockExtension.class)
public class WireMockTest {

  private static final int MOCK_SERVICE_PORT_NUMBER = 3000;

  @KnotxWiremock // will be started on a random port
  private WireMockServer mockServiceRandom;

  @Test
  public void testWiremockRunningOnPort(
      @KnotxWiremock(port = MOCK_SERVICE_PORT_NUMBER) WireMockServer server)
      throws IOException, URISyntaxException {
    // ...
  }
}

One exception applies: one WireMockServer instance will be created per identifier within test given class:

@ExtendWith(KnotxWiremockExtension.class)
public class WireMockTest {

  @KnotxWiremock
  private WireMockServer mockServiceRandom;

  @Test
  public void testWiremockEquality(
      @KnotxWiremock WireMockServer mockServiceRandom) {
    assertTrue(this.mockServiceRandom == mockServiceRandom);
  }
}

Only one WireMockServer instance with random port is created (mockServiceRandom) and injected both into instance field and method parameter.

Referencing WireMockServer ports in Knot.x configuration

With KnotxExtension, created WireMockServer instances' ports will be available for referencing in Knot.x configuration under test.wiremock.<wiremockserver_identifier>.port variables (HOCON syntax only).

KnotxArgumentConverter

Simplifies writing parameterized tests that have data source as String but expect a specific object as a parameter. Currently supports only io.knotx.dataobjects.Fragment and io.vertx.core.json.JsonObject.

Example usage:

public class ParameterizedTest {
  @ParameterizedTest
  @CsvSource(
      value = {
        "snippet_one_service_no_params.txt;{};1",
        "snippet_one_service_one_param.txt;{\"val\":1};1",
        "snippet_one_service_many_params.txt;{};1",
        "snippet_two_services.txt;{\"val\":1,\"val2\":2};2",
        "snippet_five_services.txt;{\"val\":3,\"val2\":4};5"
      },
      delimiter = ';')
  public void testWithParameters(
      @ConvertWith(KnotxArgumentConverter.class) Fragment fragment,
      @ConvertWith(KnotxArgumentConverter.class) JsonObject parameters,
      int number)
      throws Exception {
    // ...
  }
}

Bugs

All feature requests and bugs can be filed as issues on Gitub. Do not use Github issues to ask questions, post them on the User Group or Gitter Chat.

Licence

Knot.x modules are licensed under the Apache License, Version 2.0 (the "License")