Skip to content

4. Services

Jose edited this page Oct 14, 2021 · 2 revisions

The objective of services is to act as representative between the scenario and the internal managed resource:

@QuarkusApplication
static final DefaultService pingApp = new DefaultService();

This service will host the quarkus application internally, but it will also expose the functionality to interact with it:

  • withProperty: Intuitive usage of properties.

  • Service Lifecycle

The framework allows to add actions via hooks in every stage of the service lifecycle:

@QuarkusApplication
static final DefaultService pingApp = new DefaultService()
    .onPreStart(GreetingResourceTest::onPreStart);
    .onPostStart(GreetingResourceTest::onPostStart);
  • Services are startable and stoppable by default

Any Quarkus application and containers can be stopped to cover more complex scenarios. The test framework will restart the services by you before starting a new test case.

  • Services are logabble

We can verify any traces that the managed resources is logging.

@QuarkusScenario
public class DevModeMySqlDatabaseIT {

    @DevModeQuarkusApplication
    static DefaultService app = new DefaultService();

    @Test
    public void verifyLogsToAssertDevMode() {
        app.logs().assertContains("Profile dev activated. Live Coding activated");
    }
}

Services Implementations

We can add custom implementations of services to share common functionality. The test framework provides the following services:

Rest Services

There is a custom service implementation where we can use REST assured:

@QuarkusScenario
public class PingPongResourceIT {
    @QuarkusApplication
    static final RestService pingApp = new RestService();

    @Test
    public void shouldPingWorks() {
        pingApp.given().get("/ping").then().statusCode(HttpStatus.SC_OK).body(is("ping"));
    }
}

Database Services

The test framework have some utilities to ease the setup of database containers. In order to use these services, you need to add the following dependency into your Maven configuration:

<dependency>
    <groupId>io.quarkus.qe</groupId>
    <artifactId>quarkus-test-service-database</artifactId>
    <scope>test</scope>
</dependency>

The supported database services are:

  • MySQL service
  • MariaDB service
  • DB2 service
  • SQL Server service (we can't set a custom user, password and database)
  • PostgreSQL service
  • Oracle service
  • MongoDB service

All the database services contain the following methods:

  • getJdbcUrl: to return the JDBC connection URL.
  • getReactiveUrl: to return the reactive way connection URL.

Example usage:

@QuarkusScenario
public class MySqlDatabaseIT {

    @Container(image = "mysql/mysql-server:8.0", port = MYSQL_PORT, expectedLog = "port: 3306  MySQL Community Server")
    static MySqlService database = new MySqlService();

    @QuarkusApplication
    static RestService app = new RestService()
            .withProperty("quarkus.datasource.username", database.getUser())
            .withProperty("quarkus.datasource.password", database.getPassword())
            .withProperty("quarkus.datasource.jdbc.url", database::getJdbcUrl)
            .withProperty("quarkus.datasource.reactive.url", database::getReactiveUrl);
    
    // ...
}

MySQL example in here.

Infinispan Service

The test framework have some utilities to ease the setup of infinispan containers. In order to use this service, you need to add the following dependency into your Maven configuration:

<dependency>
    <groupId>io.quarkus.qe</groupId>
    <artifactId>quarkus-test-service-infinispan</artifactId>
    <scope>test</scope>
</dependency>

Example usage:

@QuarkusScenario
public class BasicInfinispanBookCacheIT extends BaseBookCacheIT {

    @Container(image = "infinispan/server:12.1", expectedLog = "Infinispan Server.*started in", port = 11222)
    static final InfinispanService infinispan = new InfinispanService();

    @QuarkusApplication
    static final RestService app = new RestService()
            .withProperty("quarkus.infinispan-client.server-list", infinispan::getInfinispanServerAddress)
            .withProperty("quarkus.infinispan-client.auth-username", infinispan.getUsername())
            .withProperty("quarkus.infinispan-client.auth-password", infinispan.getPassword());
}

For more advanced setup, we can add custom configuration as for example to enable JKS support:

@QuarkusScenario
public class UsingJksInfinispanBookCacheIT extends BaseBookCacheIT {

    @Container(image = "infinispan/server:12.1", expectedLog = "Infinispan Server.*started in", port = 11222)
    static final InfinispanService infinispan = new InfinispanService()
            .withConfigFile("jks-config.yaml")
            .withSecretFiles("jks/server.jks");

    @QuarkusApplication
    static final RestService app = new RestService()
            .withProperty("quarkus.infinispan-client.server-list", infinispan::getInfinispanServerAddress)
            .withProperty("quarkus.infinispan-client.auth-username", infinispan.getUsername())
            .withProperty("quarkus.infinispan-client.auth-password", infinispan.getPassword())
            .withProperty("quarkus.infinispan-client.trust-store", "secret::/jks/server.jks")
            .withProperty("quarkus.infinispan-client.trust-store-password", "changeit")
            .withProperty("quarkus.infinispan-client.trust-store-type", "jks");
}

For OpenShift/Kubernetes deployments and depending on the used Infinispan images, it might not be possible to communicate with the Infinispan service using the exposed routes, so we need to configure our scenario to use the internal services instead:

ts.infinispan.openshift.use-internal-service-as-url=true
ts.infinispan.kubernetes.use-internal-service-as-url=true

Full example in here.

Consul Service

Maven dependency:

<dependency>
    <groupId>io.quarkus.qe</groupId>
    <artifactId>quarkus-test-service-consul</artifactId>
    <scope>test</scope>
</dependency>

Example usage:

@QuarkusScenario
public class GreetingResourceIT {

    @Container(image = "${property.do.not.exist:quay.io/bitnami/consul:1.9.3}", expectedLog = "Synced node info", port = 8500)
    static ConsulService consul = new ConsulService();

    // ...
}

Full example in here.

gRPC Service

We can enable the gRPC feature to test Quarkus application using the @QuarkusApplication(grpc = true) annotation. This way we can verify purely gRPC applications using the GrpcService service wrapper:

@QuarkusScenario
public class GrpcServiceIT {

    static final String NAME = "Victor";

    @QuarkusApplication(grpc = true) // enable gRPC support
    static final GrpcService app = new GrpcService();

    @Test
    public void shouldHelloWorldServiceWork() {
        HelloRequest request = HelloRequest.newBuilder().setName(NAME).build();
        HelloReply response = GreeterGrpc.newBlockingStub(app.grpcChannel()).sayHello(request);

        assertEquals("Hello " + NAME, response.getMessage());
    }
}

Implement your own service

In the same way, we can add default services to ease and share common functionality. As part of the test framework, we added the consul service as an example.

public class YourCustomService extends BaseService<ConsulService> {

    // your new methods
}

And use it:

@QuarkusScenario
public class GreetingResourceIT {

    @Container // ... or @QuarkusApplication
    static final YourCustomService app = new YourCustomService();
    
    // your methods will be available
}