The goal of the project is to assist developer testing using RestAssured + Weld + JUnit 5.

Benefits:

  • Java basics, a simple and familiar environment for developers.

  • The DTO classes generated by the project can be used.

    • It is not necessary to manually assemble the request XML/JSON body.

    • Keeping tests in an active state.

  • Runnable from an IDE or via Maven on the console and CI tools.

Disadvantages:

  • Requires a more complicated test structure at first.

1. Maven structure

1.1. Project repository

The tests can reside in the same repository as the project, under a separate module named "testsuite" under the main pom. It is recommended to put the "testsuite" node in an inactivable profile, for example:

Main project pom.xml
<profiles>
  <profile>
    <id>ci</id>
    <activation>
      <activeByDefault>true</activeByDefault>
    </activation>
    <modules>
      <module>project-testsuite</module>
    </modules>
  </profile>
</profiles>
Recommended example project maven structure
project-backend
+-- project-bom
+-- project-common
+-- project-sample
\-- project-testsuite

1.2. Testsuite module

Within the testsuite module, the child POMs follow the structure defined by the project. So, if a service in the project is located under project-backend/project-sample/project-sample-service, then the tests corresponding to this service will be placed under project-backend/project-testsuite/project-testsuite-sample/project-testsuite-sample-service POM.

We further divide the testsuite Maven module for the service into submodules according to the REST interfaces found in the service. Each submodule will contain test classes corresponding to operations.

Example testsuite module structure
project-backend
+-- project-bom
+-- project-common
+-- project-sample
|   \-- project-sample-service
|       \-- src/main/java/
|           \-- hu.icellmobilsoft.project.sample.service.rest
|               +-- IUserRest.java //<1>
|               |   +-- postUser() //<2>
|               |   \-- putUser() //<3>
|               \-- ISampleRest.java //<4>
|                   \-- getSample() //<5>
\-- project-testsuite
    +-- project-testsuite-common
    \-- project-testsuite-sample
        \-- project-testsuite-sample-service
            +-- project-testsuite-sample-service-user-rest //<1>
                \-- src/test/java/
                    \-- hu.icellmobilsoft.project.testsuite.sample.service.rest
                        +-- PostUserIT //<2>
                        \-- PutUserIT //<3>
            \-- project-testsuite-sample-service-sample-rest //<4>
                \-- src/test/java/
                    \-- hu.icellmobilsoft.project.testsuite.sample.service.rest
                        \-- GetSampleIT //<5>
1 The IUserRest interface of the sample-service. It has a separate pom in the testsuite: project-testsuite-sample-service-user-rest
2 The IUserRest.postUser() operation. In the testsuite, it has its own test class under the user-rest pom: PostUserIT
3 The IUserRest._putUser() operation. In the testsuite, it has its own test class under the user-rest pom: PutUserIT
4 The ISampleRest interface in the sample-service. It has its own pom in the testsuite: project-testsuite-sample-service-sample-rest
5 IUserRest.getSample() operation. It has its own test class under the sample-rest pom in the testsuite: GetSampleIT.

2. Configuration parameters

The tests can be parameterized using microprofile-config. To achieve this, it is sufficient for the test class to extend either the BaseConfigurableWeldIT or BaseWeldUnitType abstract class. Then, you can inject the desired configuration parameter using CDI in the usual way. The configuration parameters can be set through the META-INF/roaster-defaults.yml configuration YAML file. We can create additional YAML files for profiles alongside roaster-defaults.yml, named roaster-{profile}.yml. The profiles to be executed can be set via the profile key on any default MP-Config source. Multiple profiles can be specified separated by commas (,). In this case, configuration keys are read starting from the first profile specified and continuing until the last one.

If necessary, microprofile-Config allows adding new config sources. So if the project requires it, it is possible to use configurations read from ETCD or a database.

Example:

META-INF/roaster-defaults.yml
roaster:
    test:
        sample:
            url: http://roaster-default.hu
            port: 8083
            pass: secret
META-INF/roaster-local.yml
roaster:
    test:
        sample:
            url: http://localhost
            port: 8084
META-INF/roaster-sandbox.yml
roaster:
    test:
        sample:
            url: http://roaster-sandbox.hu
GetSampleIT.java
@DisplayName("Testing Sample Rest")
@Tag(TestSuiteGroup.RESTASSURED)
public class GetSampleIT extends BaseConfigurableWeldIT {

    @Inject
    @ConfigProperty(name = "roaster.test.sample.url")
    private String sampleUrl;
    @Inject
    @ConfigProperty(name = "roaster.test.sample.port")
    private Integer samplePort;
    @Inject
    @ConfigProperty(name = "roaster.test.sample.pass")
    private Integer samplePass;
Profile running
~/project-backend/project-testsuite$ mvn verify -Dprofile=sandbox,local (1)
1 Profile running: Order of read YAMLs: roaster-sandbox.yml, roaster-local.yml, roaster-defaults.yml

Read values:

  • sampleUrl = http://roaster-sandbox.hu (roaster-sandbox.yml from config)

  • samplePort = 8084 (In roaster-sandbox.yml, there are no values, so the configuration is read from roaster-local.yml)

  • samplePass = secret (In roaster-sandbox.yml and roaster-local.yml, there are no values defined, so the configuration is read from roaster-defaults.yml)

3. Modules:

3.1. API

To use the API module, you just need to reference it in the pom.xml file

pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-api</artifactId>
</dependency>

3.1.1. Using the API

3.1.1.1. BaseBuilder

This class is intended to be the common ancestor of request builders

Example implementation
public class ExampleQueryRequestBuilder extends BaseBuilder<ExampleQueryRequest> {

    public Foo getFoo() {
        if (getDto() == null) {
            setDto(createEmpty());
        }
        return getDto().getFoo();
    }

    @Override
    public ExampleQueryRequest createEmpty() {
        return new ExampleQueryRequest();
    }
}

3.2. Jaxrs

To use the JAX-RS module, you just need to reference it in the pom.xml file.

pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-jaxrs</artifactId>
</dependency>

3.2.1. Using Jaxrs

The test files adhere to the Jakarta EE JAX-RS specification, specifically using the RestEasy implementation.

3.2.1.1. RestAssured default settings

By default, the system supports XML or JSON formats.

JSON response settings
  • The response HTTP status is 200 and "application/json" is accepted.

XML response settings
  • The response HTTP status is 200 and "application/xml" is accepted.

3.2.1.2. ResponseProcessor
Implementation

The abstract class hu.icellmobilsoft.roaster.jaxrs.response.ResponseProcessor was created to assist in testing a specific endpoint. When inheriting, you need to specify the Response type as a generic parameter and implement two methods that return strings to identify the endpoint.

  • baseUriKey: The key used in Roaster configuration to specify the base URI of the endpoint

  • path: The endpoint path. For example, in http://localhost/foo/bar, it would be: /foo/bar

Optionally, the other methods of the class can also be overridden to customize request or response processing.

Usage

Using methods like getOctetStream, postMultipartJson, postMultipartXml, etc., implemented in the ResponseProcessor, we can invoke the endpoint and automatically parse the response.

3.2.1.3. ConfigurableResponseProcessor

To avoid implementing a separate ResponseProcessor for each REST endpoint, the ConfigurableResponseProcessor class was created. This class reads necessary values from configuration, such as baseUriKey and path.

Usage

In the configuration, the baseUriKey and path should be added under a common prefix. Optionally, headers can also be specified here. In YAML format, it looks like this:

testsuite:
    rest:
        exampleService:
            example:
                baseUriKey: example-project.example-service.url
                path: /rest/exampleService/example/{id}
            headerExample:
                baseUriKey: example-project.example-service.url
                path: /rest/exampleService/example/{id}
                headers:
                    - "X-LANGUAGE: hu"
                    - "User-Agent: roaster/0.8 restassured"

You can inject the ConfigurableResponseProcessor into the test class using the @RestProcessor qualifier, specifying the configuration prefix.

import hu.icellmobilsoft.roaster.jaxrs.response.producer.impl.ConfigurableResponseProcessor;

class ExampleTest {
    @Inject
    @RestProcessor(configKey = "testsuite.rest.exampleService.example") (1)
    private ConfigurableResponseProcessor<ExampleResponse> responseProcessor;

    @Inject
    @RestProcessor(
            configKey = "testsuite.rest.exampleService.example",
            expectedStatusCode = HttpStatus.SC_BAD_REQUEST (2)
    )
    private ConfigurableResponseProcessor<ExampleResponse> badRequestResponseProcessor;

    void test() {
        MultipartFormDataOutput multipartbody = new MultipartFormDataOutput();
        multipartbody.addFormData("part1", "part1Body", javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE);
        multipartbody.addFormData("part2", "part2Body", javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE);
        String response = processor.postMultipartJson(multipartbody, String.class, "jsonEntityId");
        Assertions.assertEquals(JsonUtil.toJson(RESPONSE_DTO), response);
    }
}
1 Basic example of injecting ConfigurableResponseProcessor:
2 Optionally, the expected response status code can also be specified in the annotation (default: 200 OK).
3 In ConfigurableResponseProcessor, you can also specify the query parameters.
Own implementation

If you want to extend the functionality of ConfigurableResponseProcessor within the project, (for example: for handling login or other project-specific header operations), you can do so by first implementing AbstractConfigurableResponseProcessor:

import hu.icellmobilsoft.roaster.jaxrs.response.producer.spi.AbstractConfigurableResponseProcessor;

@Dependent
public class CustomConfigurableResponseProcessor<RESPONSE> extends AbstractConfigurableResponseProcessor<RESPONSE> {

    // We override what is necessary...
}

After this, you need to create a CDI producer that provides the configured instances:

import hu.icellmobilsoft.roaster.jaxrs.response.producer.spi.AbstractConfigurableResponseProcessorProducer;

@Dependent
public class CustomConfigurableResponseProcessorProducer<RESPONSE> extends AbstractConfigurableResponseProcessorProducer<LoginConfigurableResponseProcessor<RESPONSE>> {
    @Override
    @Produces (1)
    @RestProcessor(configKey = "")
    public CustomConfigurableResponseProcessor<RESPONSE> createCustomResponseProcessor(InjectionPoint injectionPoint) {
        return super.createConfiguredResponseProcessor(injectionPoint);
    }

    @Override
    protected CustomConfigurableResponseProcessor<RESPONSE> getBaseResponseProcessor() { (2)
        return CDI.current().select(CustomConfigurableResponseProcessor.class).get();
    }
}
1 You need to create a producer method (createCustomResponseProcessor where you can obtain a pre-configured instance when invoked)
2 You need to override the getBaseResponseProcessor method to return our type (which will be configured by createCustomResponseProcessor)

After this, you can use your own implementation, either alongside the default solution, in a test.

import hu.icellmobilsoft.roaster.jaxrs.response.producer.impl.ConfigurableResponseProcessor;

class ExampleTest {
    @Inject
    @RestProcessor(configKey = "testsuite.rest.exampleService.example")
    private CustomConfigurableResponseProcessor<ExampleResponse> customResponseProcessor;
...
}

3.2.2. Microprofile Rest Client

The MicroProfile Rest Client itself uses the JAX-RS fundamentals, so JAX-RS implementations inherently support it. For example, in the case of RestEasy, a more detailed description can be found here: MicroProfile Rest Client

The Roaster dependency provides support for using the MicroProfile Rest Client.

Simple MicroProfile Rest Client API example
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import hu.icellmobilsoft.coffee.dto.common.commonservice.BaseRequest;
import hu.icellmobilsoft.coffee.dto.exception.BaseException;

@RegisterRestClient
public interface TestMpRestApi {

    @POST
    @Path("/mp/rest/client/post")
    @Produces(value = { MediaType.APPLICATION_JSON })
    @Consumes(value = { MediaType.APPLICATION_JSON })
    String testPost(BaseRequest baseRequest) throws BaseException;
}
Using MicroProfile Rest Client API
TestMpRestApi testMpRestApiImpl = RestClientBuilder.newBuilder()
    // set URI
    .baseUri(URI.create(System.getProperty(URI_KEY)))
    // build API interface
    .build(TestMpRestApi.class);
String response = testMpRestApiImpl.testPost(DtoHelper.createBaseRequest());

3.3. RestAssured

To use the RestAssured module, you just need to reference it in the pom.xml file.

pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-restassured</artifactId>
</dependency>

3.3.1. Using RestAssured

The test files follow the following pattern:

@DisplayName("Testing XY service hu.icellmobilsoft.project.sample.service.rest.test.ISampleTestRest.postSampleRquest")
@Tag(TestSuiteGroup.RESTASSURED)
public class PostSampleRequestIT extends BaseConfigurableWeldIT {

    @Inject
    @ConfigProperty(name = "service.sample.uri")
    private String baseUri;

    @Inject
    @JSON
    private RequestSpecification requestSpecification;

    @Inject
    @JSON
    private ResponseSpecification responseSpecification;

    @ParameterizedTest
    @CsvSource({ "1, 1, 2", "2, 3, 5", })
    @ExplicitParamInjection
    void test(int a, int b, int expectedSum) {
      Assertions.assertEquals(expectedSum, a + b);
    }

    @Test
    @DisplayName("Testing opg generator json rest")
    void testSampleRest() {
        SampleRequest body = new SampleRequest();

        SampleResponse actual = RestAssured
                // given
                .given()//
                .spec(requestSpecification)//
                .config(restAssuredConfig).body(body) //
                .baseUri(baseUri) //
                // when
                .when()//
                .log().all()//
                .post("/sample")
                // then
                .then()//
                .log().all()//
                .spec(responseSpecification)//
                .extract().response().getBody().as(SampleResponse.class);
        Assertions.assertEquals(FunctionCodeType.OK, actual.getFuncCode());
    }
}
3.3.1.1. Default settings of RestAssured

By default, the system supports XML or JSON formats. The settings are managed by the following classes:

  • RestAssuredConfigProducer

  • RequestSpecificationProducer

  • ResponseSpecificationProducer

All of them can be freely modified in the implementing project.

Default settings of JSON
  • The request content-type and accept headers are "application/json".

  • The response HTTP status is 200 and "application/json" is accepted.

  • The default settings are summarized by Jackson2ObjectMapperFactoryImpl.

If there is a need for a custom ObjectMapperConfig, similar to what is generated in ObjectMapperConfigProducer, a sample solution could be:

@Inject
@JSON
private RestAssuredConfig restAssuredConfig;

((Jackson2ObjectMapperFactoryImpl) restAssuredConfig.getObjectMapperConfig().jackson2ObjectMapperFactory()).getObjectMapper()
        .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Default settings of XML
  • Encoding UTF-8.

  • The request content-type and accept headers are "application/xml".

  • The response HTTP status is 200 and "application/xml" is accepted.

3.3.1.2. ResponseProcessor
Implementation

The ResponseProcessor abstract class was created to assist in testing a specific endpoint. When subclassing, you need to specify the response type in a generic parameter and implement two methods that return strings to identify the endpoint:

  • baseUriKey: The key used in the Roaster configuration that specifies the baseURI of the endpoint

  • path: The endpoint path. For example, in http://localhost/foo/bar, it would be: /foo/bar

Optionally, other methods of the class can also be overridden to customize the processing of requests or responses. For example, by overriding the createRequestSpecification method, you can modify the request headers, cookies, etc.

Usage

Using the getJson, postJson, getXml, postXml methods of the ResponseProcessor implementation, we can invoke the endpoint and automatically parse the response.

Example
public class ExampleQueryResponseProcessor extends ResponseProcessor<ExampleQueryResponse> {

    @Override
    public String baseUriKey() {
        return "example-project.example-service.url"; // value e.g: http://localhost:8080
    }

    @Override
    public String path() {
        return "/example/foo/{id}";
    }

    @Override // Example of adding an extra header
    protected RequestSpecification createRequestSpecification(RequestSpecification initRequestSpecification) {
        return super.createRequestSpecification(initRequestSpecification)
                .header("Authorization", "Basic dXNlcjpwYXNz");
    }

    public ExampleQueryResponse queryWithJson(String id) {
        return getJson(ExampleQueryResponse.class, id);
    }

    public ExampleQueryResponse queryWithXml(String id) {
        return getXml(ExampleQueryResponse.class, id);
    }
}
3.3.1.3. ConfigurableResponseProcessor

To avoid implementing a separate ResponseProcessor for each REST endpoint, the ConfigurableResponseProcessor class has been created. It reads necessary values from configuration, such as baseUriKey and path.

Usage

To configure, you need to include baseUriKey and path under a common prefix. Optionally, headers can also be provided. In YAML format, it looks like this:

testsuite:
    rest:
        exampleService:
            example:
                baseUriKey: example-project.example-service.url
                path: /rest/exampleService/example/{id}
            headerExample:
                baseUriKey: example-project.example-service.url
                path: /rest/exampleService/example/{id}
                headers:
                    - "X-LANGUAGE: hu"
                    - "User-Agent: roaster/0.5 restassured"

In the test class, you can inject the ConfigurableResponseProcessor using the @RestProcessor qualifier, providing the configuration prefix:

import hu.icellmobilsoft.roaster.restassured.response.producer.impl.ConfigurableResponseProcessor;

class ExampleTest {
    @Inject
    @RestProcessor(configKey = "testsuite.rest.exampleService.example") (1)
    private ConfigurableResponseProcessor<ExampleResponse> responseProcessor;

    @Inject
    @RestProcessor(
            configKey = "testsuite.rest.exampleService.example",
            expectedStatusCode = HttpStatus.SC_BAD_REQUEST (2)
    )
    private ConfigurableResponseProcessor<ExampleResponse> badRequestResponseProcessor;

    void test() {
        responseProcessor.setQueryParams(Map.of("foo", "42")); (3)
        ExampleResponse response = responseProcessor.getJson(ExampleResponse.class, "XXX");
        // asserts...
    }
}
1 Basic example of injecting ConfigurableResponseProcessor
2 Optionally, the expected response status code can also be specified in the annotation (default: 200 OK)
3 In ConfigurableResponseProcessor, you can also specify query parameters
Own implementation

If you want to extend the functionality of ConfigurableResponseProcessor within your project (e.g., for handling login or other project-specific headers), you can first implement AbstractConfigurableResponseProcessor:

import hu.icellmobilsoft.roaster.restassured.response.producer.spi.AbstractConfigurableResponseProcessor;

@Dependent
public class LoginConfigurableResponseProcessor<RESPONSE> extends AbstractConfigurableResponseProcessor<RESPONSE> {
    @Inject
    private LoginHandler loginHandler;

    @Override
    protected RequestSpecification createRequestSpecification(RequestSpecification initRequestSpecification) {
        loginHandler.login();
        return super.createRequestSpecification(initRequestSpecification)
                .headers(loginHandler.loginHeaders());
    }
}

After this, you need to create a CDI producer that serves the configured instances:

import hu.icellmobilsoft.roaster.restassured.response.producer.spi.AbstractConfigurableResponseProcessorProducer;

@Dependent
public class LoginConfigurableResponseProcessorProducer<RESPONSE> extends AbstractConfigurableResponseProcessorProducer<LoginConfigurableResponseProcessor<RESPONSE>> {
    @Override
    @Produces (1)
    @RestProcessor(configKey = "")
    public LoginConfigurableResponseProcessor<RESPONSE> createLoginResponseProcessor(InjectionPoint injectionPoint) {
        return super.createConfiguredResponseProcessor(injectionPoint);
    }

    @Override
    protected LoginConfigurableResponseProcessor<RESPONSE> getBaseResponseProcessor() { (2)
        return CDI.current().select(LoginConfigurableResponseProcessor.class).get();
    }
}
1 You need to create a producer (invoking getConfiguredResponseProcessor will provide you with a configured instance)
2 Override the getBaseResponseProcessor method to return our type (this will be configured by getConfiguredResponseProcessor).

After this, you can use your own implementation, either alongside the default solution, even in a single test:

import hu.icellmobilsoft.roaster.restassured.response.producer.impl.ConfigurableResponseProcessor;

class ExampleTest {
    @Inject
    @RestProcessor(configKey = "testsuite.rest.exampleService.example")
    private LoginConfigurableResponseProcessor<ExampleResponse> loginResponseProcessor;

    @Inject
    @RestProcessor(
            configKey = "testsuite.rest.exampleService.example",
            expectedStatusCode = HttpStatus.SC_UNAUTHORIZED
    )
    private ConfigurableResponseProcessor<ExampleResponse> noLoginResponseProcessor;
}

3.4. REDIS

To use the Redis module, you just need to reference it in the pom.xml file.

pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-redis</artifactId>
</dependency>

3.4.1. REDIS Configuration Parameters

The necessary data for accessing Redis can be parameterized using microprofile-config.

Example:

META-INF/microprofile-config.properties
coffee.redis.default.host=example.com
coffee.redis.default.port=6379
coffee.redis.default.password=redispass
coffee.redis.default.database=0
coffee.redis.default.pool.maxtotal=128
coffee.redis.default.pool.maxidle=32
META-INF/roaster-defaults.yml
coffee:
    redis:
        default:
            host: example.com
            port: 6379
            password: redispass
            database: 0
            pool:
                maxtotal: 128
                maxidle: 32

3.4.2. Using REDIS

First, you need to inject the handler class.

Injecting the handler
    @Inject
    private RedisHandler redisHandler;
3.4.2.1. Operations of RedisHandler

Through RedisHandler, operations are accessed, creating a RedisService using CDI each time they are invoked. RedisService is instantiated with the data defined by redisConfigKey, and operations are forwarded to it.

The redisConfigKey is the third tag in the keys specified in microprofile-config.properties, e.g: coffee.redis.default.host → default

Operations taken from RedisService:

  • getRedisData(redisConfigKey, valueKey, class)

  • getRedisDataOpt(redisConfigKey, valueKey, class)

  • setRedisData(redisConfigKey, valueKey, redisData)

  • setRedisDataExp(redisConfigKey, valueKey, secondsToExpire, redisData)

  • removeRedisData(redisConfigKey, valueKey)

  • removeAllRedisData(redisConfigKey, List<valueKey>)

  • removeAllRedisData(redisConfigKey)

Example of operation calls
String data = redisHandler.getRedisData("default", "1234", String.class);
String newData = redisHandler.setRedisData("default", "1234", "new data");
redisHandler.removeAllRedisData("default");

3.5. Oracle DB

To use the Oracle DB module, you just need to reference it in the pom.xml file

pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-oracle</artifactId>
</dependency>

3.5.1. Oracle DB configuration parameters

With MicroProfile Config, the necessary data for accessing Oracle DB can be parameterized.

Examples:

META-INF/microprofile-config.properties
roaster.datasource.oracle.application.url=jdbc:oracle:thin:@//oracle.icellmobilsoft.hu:1521/sample
roaster.datasource.oracle.application.user=db_user
roaster.datasource.oracle.application.password=db_pass
META-INF/roaster-defaults.yml
roaster:
    datasource:
        oracle:
            application:
                url: jdbc:oracle:thin:@//oracle.icellmobilsoft.hu:1521/sample
                user: db_user
                password: db_pass
                maximumPoolSize: 10 (1)
1 (Optional) The maximum value of the connection pool (default: 5)

3.5.2. Using OracleJDBCSelectorService

First, you need to inject the service class.

Injecting OracleJDBCSelectorService
    @Inject
    @DBConnection(configKey = DBConstants.Schema.APPLICATION)
    private OracleJDBCSelectorService service;
3.5.2.1. Operations of OracleJDBCSelectorService

Through OracleJDBCSelectorService, operations can be accessed. When these operations are invoked and if there is no open connection yet, it creates a new one with the database. At the end of the operation, it closes the connection.

Methods starting with "select" accept only SQL statements that begin with "SELECT"! The selectFirst method returns only the first row if there are multiple results from the database.
  • selectFirstStringValue(sql select string, columnName)

  • selectFirstIntegerValue(sql select string, columnName)

  • selectFirstBooleanValue(sql select string, columnName)

  • selectFirstBigDecimalValue(sql select string, columnName)

  • selectFirstDateValue(sql select string, columnName)

  • selectRowCount(sql select string)

  • selectFirstObject(sql select string, converter)

  • selectAllObjects(sql select string, converter)

Examples of operation calls
String name = service.selectFirstStringValue("SELECT * FROM EXAMPLE WHERE X__ID = '0'", "NAME");
int rowCount = service.selectRowCount("SELECT * FROM EXAMPLE WHERE X__ID = '0'");
List<Example> exampleList = service.selectAllObjects("SELECT * FROM EXAMPLE", exampleConverter::convert);
Examples of Converter
private T convert(ResultSet rs) throws BaseException {
    try {
        T t = new T();
        t.setId(rs.getString("X__ID"));
        t.setName(rs.getString("NAME"));
        return t;
    } catch (SQLException e) {
        throw new TechnicalException(e.getLocalizedMessage());
    }
}
3.5.2.2. Methods necessary for managing connections
  • getJdbcConnection()

  • setJdbcConnection(JDBCConnection)

  • close

3.6. TM4J

The purpose of this module is to automatically report the results of automated test cases to a configured TM4J (Test Management for Jira) server.

3.6.1. Configuration

META-INF/roaster-defaults.yml
roaster:
   tm4j:
      enabled: true (1)
      projectKey: ABC (2)
      testCycleKey: ABC-C1 (3)
      environment: sandbox (4)
      server/mp-rest/url: https://jira.example.com (5)
      server/basicAuthToken: ZXhhbXBsZS11c2VyOnNlY3JldA== (6)
      server/userName: (7)
      server/password:
      cycle:
         foo: ABC-C2 (8)
         bar: ABC-C3
1 The TM4J server call can be disabled with this flag (default: false).
2 Jira project key. It is usually the prefix of Jira issues.
3 TM4J test cycle key. For example, ABC-C1 where ABC is the project key.
4 (Optional) The name of the current environment where the tests are running. (Needs to be created in Jira)
5 TM4J server URL.
6 base64(userName + ":" + password)
7 You can use either the server/userName + server/password or server/basicAuthToken parameters to provide server authentication information.
8 Possibility to specify different TM4J test cycles per test tag. If a test is tagged with a tag that is listed here (e.g., @Tag("foo") in JUnit 5), the result will be reported to the specified cycle instead of the default test cycle (testCycleKey configuration key). If a test has multiple tags, the result will be reported to each corresponding cycle.
For security reasons, it is recommended not to include the basicAuthToken and password directly in the configuration file. Instead, you can pass them as command-line arguments to Maven. For example: $ mvn test -Droaster.tm4j.server/password=secret

3.6.2. Usage with JUnit 5

To use the module, you need to add the following Maven dependency to your pom.xml:

pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-tm4j-junit5</artifactId>
</dependency>

After that, you just need to annotate the test classes/methods that you want to report the results of:

@ExtendWith(Tm4jExtension.class) // Activate the TM4J extension on the test class
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ExampleTest extends BaseWeldUnitType {
    @Test
    @TestCaseId("ABC-T1") // Link the test case to TM4J
    void testOne() {
        assertTrue(1 + 1 == 2);
    }
}

3.7. Zephyr Scale Cloud

The purpose of this module is to automatically report the results of automated test cases to a Zephyr Scale Cloud environment on a JIRA interface running in the cloud. Depending on the outcome of the tests it also sets the same result (successful or unsuccessful) for all the test steps belonging to the test cases in JIRA. For test cases, we also handle nested test cases when the given test case its test steps contain more test cases. The maximum depth of this test structure can be parameterized.

3.7.1. Configuration

META-INF/roaster-defaults.yml
roaster:
   zephyr:
      enabled: true (1)
      projectKey: ABC (2)
      testCycleKey: ABC-C1 (3)
      environment: sandbox (4)
      server/mp-rest/url: https://jira.example.com (5)
      server/authToken: ZXhhbXBsZS11c2VyOnNlY3JldA== (6)
      server/email: tim@examplemail.com (7)
      server/apiToken: dGltQGV4YW1wbGVtYWlsLmNvbTpteUFwaUtleQ== (8)
      cloud/bearerToken: jwtBearerToken (9)
      cycle:
         foo: ABC-C2 (10)
         bar: ABC-C3
      testSteps:
         enabled: true (11)
         testCaseDepth: 3 (12)
         maxResults: 100 (13)
1 The Zephyr server call can be enabled with this flag (default: false).
2 Jira project key. It is usually the prefix of Jira issues.
3 Zephyr test cycle key. For example, ABC-C1 where ABC is the project key.
4 (Optional) The name of the current environment where the tests are running. (Needs to be created in Jira)
5 Remote Jira server URL.
6 base64(email + ":" + apiToken)
7 Email address associated with the Jira user. This user should be used to generate the apiToken and bearerToken.
8 ApiToken for using remote Jira functionalities. It needs to be generated in the Jira profile interface.
9 Zephyr Scale Bearer Token. It needs to be generated on the remote Jira interface, in your own profile section.
10 Possibility to specify different Zephyr test cycles per test tag. If a test is tagged with a tag that is listed here (e.g., @Tag("foo") in JUnit 5), the result will be reported to the specified cycle instead of the default test cycle (testCycleKey configuration key). If a test has multiple tags, the result will be reported to each corresponding cycle.
11 The detailed Zephyr server call with test steps data can be enabled with this flag (default: false).
12 Depth limit for nested test cases. Default 3, if not specified.
13 Maximum number of results to return in Zephyr API teststeps. Default 100, if not specified.

For more detailed information on generating tokens, please refer to the following links:

For security reasons, it is recommended not to include the authToken and api tokens directly in the configuration file. Instead, you can pass them as command-line arguments to Maven. For example: $ mvn test -Droaster.zephyr.server/authToken=secret

3.7.2. Usage with JUnit 5

To use the module, you need to add the following Maven dependency to your pom.xml:

pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-zephyr-junit5</artifactId>
</dependency>

After that, you just need to annotate the test classes/methods that you want to report the results of:

@ExtendWith(ZephyrExtension.class) // Activate the Zephyr extension on the test class
@TestInstance(Test

Instance.Lifecycle.PER_CLASS)
class ExampleTest extends BaseWeldUnitType {
    @Test
    @TestCaseId("ABC-T1") // Link the test case to Zephyr
    void testOne() {
        assertTrue(1 + 1 == 2);
    }
}

3.8. Selenide

To use the Selenide module, you just need to reference it in the pom.xml file.

pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-selenide-api</artifactId>
</dependency>

The Selenide module is not compatible with Internet Explorer!!

3.8.1. Selenide Configuration Parameters

The Selenide and WebDriver parameters can be configured using microprofile-config.

Examples:

META-INF/microprofile-config.properties
roaster.selenide.homepage=https://google.com
roaster.selenide.timeout=3000
roaster.selenide.selenium.url=https://localhost:9999/wd/hub
roaster.selenide.browser.type=chrome
roaster.selenide.browser.headless=true
roaster.selenide.browser.device=Galaxy S5
roaster.selenide.browser.decision.width=1024
roaster.selenide.browser.decision.height=768
META-INF/roaster-defaults.yml
# Minimal Roaster config for Selenide
roaster:
  selenide:
    homepage: https://google.com # URL of the page that opens in the browser
    timeout: 3000 # default timeout for WebDriver
    selenium:
      url: # URL of wd hub if tests need to be run with remote WebDriver
    browser:
      type: chrome # possible values: chrome, edge, opera, safari
      headless: false
      device: # Galaxy S5 or iPad Pro (See options here: https://github.com/DevExpress/device-specs/blob/master/devices.md)
      decision:
        width: # example: 1024
        height: # example: 768

It’s important to note that if roaster.selenide.selenium.url is not filled, it won’t run in remote mode.

It’s important to note that if roaster.selenide.browser.device is filled, the Chrome browser will be launched, emulating the device with the specified name. It’s important to remember that since the emulated device has a fixed screen size, that will be used, and it won’t take into account the resolution settings.

It’s important to note that if roaster.selenide.browser.device.width and roaster.selenide.browser.device.height are filled, the browser won’t be adjusted to the screen.

3.8.2. Usage Example

GoogleIcellMobilsoftTest sample class
@Tag(TestSuiteGroup.SELENIDE)
@Disabled("Only for testing!!!")
public class GoogleIcellMobilsoftTest extends BaseSelenideTestCase {

    @Test
    @DisplayName("Search 'icellmobilsoft' word on Google.com")
    @Disabled("Only for testing!!!")
    public void testGoogleWithIcellmobilsoftSearch() {
        // You don't have to use Selenide.open(<url>), it comes from the configuration and does it automatically
        SelenideElement qInput = Selenide.$("input[name='q']");
        qInput.setValue("icellmobilsoft").pressEnter();

        SelenideElement result = Selenide.$("div[id='search']");
        ElementsCollection resultList = result.$$("div[class='g'] a[href='https://icellmobilsoft.hu/hu/'");
        resultList.first().shouldHave(Condition.visible);
    }
}

3.8.3. Selenide Angular Module

The purpose of this module is to support more complex components used in Angular.

To use it, you need

to include the following Maven module:
pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-selenide-angular</artifactId>
</dependency>

Currently supported components:

3.8.3.1. <mat-checkbox>
example
MatCheckbox checkbox = new MatCheckbox(Selenide.$(MatCheckbox.TAG_NAME));
assertFalse(checkbox.getValue());
checkbox.toggle();
assertTrue(checkbox.getValue());
3.8.3.2. <mat-radio-group>
example
MatRadioGroup radioGroup = new MatRadioGroup(Selenide.$(MatRadioGroup.TAG_NAME));
assertTrue(radioGroup.getSelected().isEmpty());
radioGroup.select("Option 2");
assertEquals("Option 2", radioGroup.getSelected().orElseThrow());
assertIterableEquals(List.of("Option 1", "Option 2"), radioGroup.getOptions());
3.8.3.3. <mat-select>
example
MatSelect multiSelect = new MatSelect(Selenide.$("#select-multiple " + MatSelect.TAG_NAME));
multiSelect.select("Onion", "Pepperoni");
assertIterableEquals(List.of("Onion", "Pepperoni"), multiSelect.getSelected());
multiSelect.select("Tomato");
assertIterableEquals(List.of("Tomato"), multiSelect.getSelected());
assertEquals(6, multiSelect.getOptions().size());
3.8.3.4. <mat-slide-toggle>
example
MatSlideToggle checkbox = new MatSlideToggle(Selenide.$(MatSlideToggle.TAG_NAME));
assertFalse(checkbox.getValue());
checkbox.toggle();
assertTrue(checkbox.getValue());

3.9. Hibernate

To use the Hibernate module, you first need to reference the following dependency in your pom.xml file:

pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-hibernate</artifactId>
</dependency>

In addition, you also need to add the appropriate JDBC driver, which may vary depending on the usage context. It is recommended to use the following:

pom.xml in case of Oracle19
        <dependency>
            <groupId>com.oracle.ojdbc</groupId>
            <artifactId>ojdbc10</artifactId>
            <version>19.3.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.oracle.ojdbc</groupId>
            <artifactId>orai18n</artifactId>
            <version>19.3.0.0</version>
        </dependency>
pom.xml in case of Mysql
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
pom.xml in case of H2
        <!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
        </dependency>
pom.xml in case of PostgreSQL
        <!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
        <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.2.18</version>
        </dependency>

In addition, the following configurations need to be performed:

3.9.1. Hibernate configuration parameters

To use Hibernate, it is necessary to create the persistence.xml file at the project level within the META-INF folder among resources. If it is created with the name defaultPU (as in the example), then the EntityManager can be injected without any extra annotations.

Example:

META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">

    <persistence-unit name="defaultPU">
        <mapping-file>META-INF/jpa-entity.xml</mapping-file>
    </persistence-unit>
</persistence>
In the persistence.xml, it is necessary to specify the entities regardless! For further custom settings, you can find a simple description here: https://vladmihalcea.com/jpa-persistence-xml/ Settings provided through microprofile-config will be used, overriding identical properties in the persistence.xml. If a specific part is not configured in microprofile-config but is configured in persistence.xml, then the settings in persistence.xml will be applied.

Hibernate configuration settings can be parameterized using microprofile-config.

Example:

META-INF/microprofile-config.properties
roaster.hibernate.defaultPU.jdbc.driver=oracle.jdbc.OracleDriver
roaster.hibernate.defaultPU.jdbc.url=jdbc:oracle:thin:@//localhost:1521/XE
roaster.hibernate.defaultPU.jdbc.user=db_username
roaster.hibernate.defaultPU.jdbc.password=*****
roaster.hiberante.defaultPU.jdbc.log_warnings=true
roaster.hibernate.defaultPU.hibernate.default_schema=user_schema
roaster.hibernate.defaultPU.hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
roaster.hibernate.defaultPU.hibernate.show_sql=true
roaster.hibernate.defaultPU.hibernate.format_sql=true
roaster.hiberante.defaultPU.hibernate.log_session_metrics=true
roaster.hiberante.defaultPU.hibernate.generate_statistics=true
META-INF/roaster-defaults.yml
  roaster:
    hibernate:
      defaultPU:
        jdbc:
          driver: oracle.jdbc.OracleDriver
          url: jdbc:oracle:thin:@//localhost:1521/XE
          user: db_username
          password: *****
          log_warnings: true
        hibernate:
          default_schema: user_schema
          dialect: org.hibernate.dialect.Oracle12cDialect
          show_sql: true
          format_sql: true
          log_session_metrics: true
          generate_statistics: true

3.9.2. Example of usage

HibernateTest example class
public class HibernateTest extends BaseWeldUnitType {

    @Inject
    // This applies in the background: @HibernatePersistenceConfig(persistenceUnitName = "defaultPU")
    private EntityManager userEntityManager;

    @Inject
    @HibernatePersistenceConfig(persistenceUnitName = "otherPu")
    private EntityManager otherEntityManager;

    @Test
    public void testEntityManager() {
        Assertions.assertNotNull(userEntityManager);
        SecurityUser securityUser = userEntityManager.find(SecurityUser.class, "0");
        Assertions.assertNotNull(securityUser);
        Assertions.assertNotNull(otherEntityManager);
        OtherEntity other = userEntityManager.find(OtherEntity.class, "0");
        Assertions.assertNotNull(other);
    }


    @Test
    public void testUseEntityManager() {
        Assertions.assertNotNull(userEntityManager);

        User user = userEntityManager.find(User.class,"FD34123");
        Assertions.assertNotNull(user);

        CriteriaBuilder builder = userEntityManager.getCriteriaBuilder();
        CriteriaQuery<User> criteriaQuery = builder.createQuery(User.class);
        Root<User> root = criteriaQuery.from(User.class);
        criteriaQuery.select(root);
        List<Order> os = new ArrayList<>();
        os.add(builder.asc(root.get(User_.creationDate)));
        criteriaQuery.orderBy(os);
        TypedQuery<User> query = userEntityManager.createQuery(criteriaQuery);
        List<User> resultList = query.getResultList();
        // Assertions resultList

        CriteriaQuery<Long> countCriteriaQuery = builder.createQuery(Long.class);
        Root<User> countRoot = countCriteriaQuery.from(User.class);
        countCriteriaQuery.select(builder.count(countRoot));
        TypedQuery<Long> countQuery = userEntityManager.createQuery(countCriteriaQuery);
        Long count = countQuery.getSingleResult();
        // Assertions count
    }

}

3.10. Mongo DB

To use the MongoDB module, you just need to reference it in the pom.xml file

pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-mongodb</artifactId>
</dependency>

3.10.1. Configuration parameters

The necessary data for accessing MongoDB can be parameterized using microprofile-config.

Example:

META-INF/roaster-defaults.yml
coffee:
    mongodb:
        default: (1)
            database: foobar
            uri: mongodb://user:pass@localhost:27017/?ssl=false (2)
1 This is the configKey that needs to be used in the @MongoClientConfiguration annotation.
2 https://docs.mongodb.com/manual/reference/connection-string/

The database and url are mandatory. Additional tuning settings also exist, which can be found in the coffee documentation.

3.10.2. Usage

class MongoExampleIT extends BaseConfigurableWeldIT {

    @Inject
    @MongoClientConfiguration(configKey = "default")
    private Instance<MongoDbClient> mongoDbClientInstance;

    @Test
    void test() throws BaseException {
        MongoDbClient mongoDbClient = mongoDbClientInstance.get();

        // Specifying the collection
        mongoDbClient.initRepositoryCollection("example_collection");

        // Inserting an element
        Object object = Map.of("foo", "bar");
        BasicDBObject dtoDocument = MongoUtil.jsonToBasicDbObject(MongoJsonUtil.toJson(object));
        mongoDbClient.insertOne(dtoDocument);

        // Querying the ID of the inserted element
        String id = dtoDocument.getString("_id");

        // Searching with filter
        BasicDBObject filter = new BasicDBObject();
        filter.put("_id", new ObjectId(id));
        BasicDBObject result1 = mongoDbClient.findFirst(filter);
        System.out.println(result1);

        // Searching with ID
        BasicDBObject result2 = mongoDbClient.findById(id);
        System.out.println(result2);
    }
}

3.11. TestDoc

The purpose of the TestDoc module is to generate AsciiDoc documentation from test classes.

3.11.1. Usage

To activate the TestDoc module, you need to reference the following dependency in your pom.xml:

pom.xml
<dependency>
    <groupId>hu.icellmobilsoft.roaster</groupId>
    <artifactId>roaster-testdoc</artifactId>
</dependency>

Afterwards, annotate the test classes you want to document with the @TestDoc annotation.

Once you’ve done this, by default, during compilation, the generated AsciiDoc will be included in the resulting .jar file under the name META-INF/test_doc.adoc. Each test class appears in a separate table in the generated document.

3.11.2. Example

Input:

@TestDoc(titleHeadingLevel = 3) (1)
@DisplayName("Example test class")
class TestDocExampleTest {

    @Test (2)
    @DisplayName("Example test method in TestDocExampleTest class")
    @Disabled("only for example")
    void exampleTestMethod() {
        Assertions.assertTrue(true);
    }

    @ParameterizedTest (2)
    @NullSource
    @DisplayName("Example parameterized test method in TestDocExampleTest class")
    @Disabled("only for example")
    void exampleParameterizedTestMethod(String param) {
        Assertions.assertNull(param);
    }
}




@TestDoc(titleHeadingLevel = 3)  (1)
@DisplayName("Testing TestDoc functions")
class TestDocTest {

    @Test (2)
    @DisplayName("Generated file should contain the test data")
    void generatedFileShouldContainTestData() throws URISyntaxException, IOException {
        URL generatedFileUrl = getClass().getResource("/" + TestDocConfig.DEFAULT_OUTPUT_PATH + TestDocConfig.DEFAULT_OUTPUT_FILE_NAME);
        assertNotNull(generatedFileUrl);

        String generatedFile = Files.readString(Paths.get(generatedFileUrl.toURI()));

        // classes
        assertTrue(generatedFile.contains("Testing TestDoc functions"));
        assertTrue(generatedFile.contains("Example test class"));

        // methods
        assertTrue(generatedFile.contains("generatedFileShouldContainTestData"));
        assertTrue(generatedFile.contains("exampleTestMethod"));

        // display names
        assertTrue(generatedFile.contains("Generated file should contain the test data"));
        assertTrue(generatedFile.contains("Example test method in TestDocExampleTest class"));
    }
}
1 The generation is activated on a class using the @TestDoc annotation. The titleHeadingLevel parameter is used to set the heading level of the generated table’s title row, and its specification is optional. If specified, the value must fall within the interval [0, 5]. If not specified or if the provided value falls outside the interval, the default value will be 2.
2 Methods annotated with @Test and @ParameterizedTest will be included in the generated table.

Each class will have a separate table generated in the resulting document. The order of tables will follow the names of the test classes. The heading of the tables will primarily be based on the @DisplayName value of the class. If this is not filled out, then it will default to the class name. The tables will contain the test method name and its corresponding @DisplayName value, if provided.

Output:

3.11.2.1. Example test class
Method name Display name

exampleTestMethod

Example test method in TestDocExampleTest class

exampleParameterizedTestMethod

Example parameterized test method in TestDocExampleTest class

3.11.2.2. Testing TestDoc functions
Method name Display name

generatedFileShouldContainTestData

Generated file should contain the test data

3.11.3. Configuration

Since the generation uses an annotation processor, you can configure it during compilation using the -A switch. For Maven, this can be done via maven-compiler-plugin:

pom.xml example
<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <compilerArgs>
                    <arg>-Aroaster.testDoc.outputDir=${project.basedir}/../docs/</arg> (1)
                    <arg>-Aroaster.testDoc.outputFileName=${project.name}_test_doc.adoc</arg> (2)
                    <arg>-Aroaster.testDoc.outputToClassPath=false</arg> (3)
                </compilerArgs>
            </configuration>
        </plugin>
    </plugins>
</build>
1 The folder where the generated file will be placed. Default: META-INF/
2 The name of the generated file. Default: test_doc.adoc
3 Should the generated file be placed on the classpath, meaning do we want it to be included in the resulting JAR file? Default: true

4. Migration Description

Next section describes the changes between releases.

4.1. 0.1.0 → 0.2.0

4.1.2. Changes

4.1.2.1. roaster-restassured
  • The default JSON object mapper now serializes time values to ISO format instead of the previous "1600871093.907000000"

  • The deprecated option has been removed

@Inject
private RestAssuredConfig restAssuredConfig;

From now on, it can only be used with the @JSON qualifier

@Inject
@JSON
private RestAssuredConfig restAssuredConfig;
  • The Weld container runs in enableDiscovery() mode, so it is not necessary to add classes separately

  • hu.icellmobilsoft.roaster.restassured.BaseConfigurableWeldIT.configureWeld() has been deprecated

4.1.2.2. Migration

From the IT tests, manually added beans need to be removed, otherwise a "Bean name is ambiguous." error message will be received

@Deprecated
@Override
protected void configureWeld(Weld weld) {
    weld.addBeanClasses(SampleAction.class);
}
4.1.2.3. roaster-weldunit
  • The Weld container runs in enableDiscovery() mode, so it is not necessary to add classes separately

  • hu.icellmobilsoft.roaster.restassured.BaseConfigurableWeldIT.configureWeld() has been deprecated

4.1.2.4. Migration

In the unit tests, manually added beans need to be removed; otherwise, you will receive a "Bean name is ambiguous." error message

@Deprecated
@Override
protected void configureWeld(Weld weld) {
    weld.addBeanClasses(SampleAction.class);
}

4.2. 0.2.0 → 0.3.0

4.2.1. Changes

4.2.1.1. General

coff:ee upgrade 1.2.1 → 1.3.0

4.2.1.2. roaster-hibernate

In the EntityManagerProducer class, injection of EntityManagerFactory has been removed, and the produceDefaultEntityManager() method now creates it internally when needed So from now on, the EntityManagerFactory with default settings is only created when injecting EntityManager without the @HibernatePersistenceConfig annotation

4.2.1.3. Migration

The changes do not require any migration work and are backwards compatible

4.2.1.4. roaster-tm4j
  • Bugfix: Running more test maven no longer cause exception

  • Bugfix: TM4J Executed by value is reloaded by the tests

  • They load the TM4J environment value based on the test configuration parameter

4.2.1.5. Migration
  • The test class (or its superclass) must be annotated with @TestInstance(TestInstance.Lifecycle.PER_CLASS)

  • The value provided in the roaster.tm4j.environment configuration parameter must be created in the TM4J interface if it does not exist

4.3. 0.3.0 → 0.4.0

4.3.1. Changes

4.3.1.1. General

coff:ee upgrade 1.3.0 → 1.5.0

4.3.1.2. roaster-tm4j
  • Bugfix: In TM4J, we use the userKey instead of userName to load the Executed by value

4.3.1.3. roaster-restassured
  • The ResponseProcessor has been enhanced with the putXml and putJson methods

4.3.1.4. Migration

The changes do not require any migration work and are backwards compatible

4.3.1.5. roaster-api
  • The FileUtil has been moved to the newly created roaster-common-util module

4.3.1.6. Migration

The dependency and package changes related to FileUtil need to be carried over

4.3.1.7. roaster-selenide
  • Bugfix: Improvement in reading configuration parameters: headless browser run

  • Selenide version update 5.15.0 → 5.22.2

4.4. 0.4.0 → 0.5.0

4.4.1. Changes

4.4.1.1. General

coff:ee upgrade 1.5.0 → 1.6.0

4.4.1.2. roaster-restassured
  • Injectable ResponseProcessor configurable from Microprofile Config (@RestProcessor, ConfigurableResponseProcessor)

  • The testOpenAPI method in the OpenAPITestHelper class has been enhanced with an expectedOpenapiVersion parameter to allow project-wide configuration. With overloading, the original syntax is preserved, calling it defaults to 3.0.3, which is the expected openapi version.

  • Two overrideable methods decorateRequestSpecification and decorateValidatableResponse have been introduced in the OpenAPITestHelper class. By default, the class handles logging for requests and responses. Logging can be disabled or configured at the project level by subclassing the class.

    Example of disabling response logging
    @Model
    @Alternative
    @Priority(Interceptor.Priority.APPLICATION + 10)
    public class ProjectOpenApiTestHelper extends OpenAPITestHelper {
    
        @Override
        protected ValidatableResponse decorateValidatableResponse(ValidatableResponse validatableResponse) {
    //      return validatableResponse.log().all();
            return validatableResponse;
        }
    }
4.4.1.3. Migration
  • In OpenAPITestHelper.testOpenAPI(java.lang.String), assertions are now run against 3.0.3 OpenAPI version instead of 3.0.1. If this does not meet the project requirements, the OpenAPITestHelper.testOpenAPI(java.lang.String, java.lang.String) method can be used to override the expected version.

    Example of disabling response logging
        @Test
        @DisplayName("Testing Sampple service /openapi")
        void testOpenapi() {
    //    Default openapi test expected openapi version = 3.0.3
    //    openAPITestHelper.testOpenAPI(projectSampleServiceBaseUri);
    //    Openapi test with custom openapi version
          openAPITestHelper.testOpenAPI(projectSampleServiceBaseUri, "3.0.0");
        }

4.5. 0.5.0 → 0.6.0

4.5.1. Changes

4.5.1.1. General
BOM version upgrades
  • coff:ee 1.6.0 → 1.8.0

  • microprofile 3.3 → 4.0

  • smallrye-config 1.8.5 → 2.0.2

Migration

The changes do not require any migration work and are backwards compatible

4.5.1.2. roaster-restassured
  • The ResponseProcessor has been enhanced with the deleteXml and deleteJson methods

4.5.1.3. Migration

The changes do not require any migration work and are backwards compatible

4.5.1.4. roaster-selenide
  • Selenide upgrade 5.22.2 → 6.1.2

4.5.1.5. Migration
4.5.1.6. roaster-selenide-angular
  • The module has been created, including the following components: MatCheckbox, MatRadioGroup, MatSelect, MatSlideToggle

4.5.1.7. Migration

The changes do not require any migration work and are backwards compatible

roaster-mongodb
  • The module has been created

Migration

The changes do not require any migration work and are backwards compatible

roaster-oracle
  • DB connection pool was introduced; the maximumPoolSize config key was created

Migration

The changes do not require any migration work and are backwards compatible

4.6. 0.6.0 → 0.7.0

4.6.1. Changes

4.6.1.1. General
BOM version upgrades
  • junit5 v5.6.2 → v5.8.2

  • selenide v6.1.2 → v6.3.0

4.6.1.2. roaster-common-config
  • The call public YamlConfigSource(String name, InputStream stream, int defaultOrdinal) throws IOException used in RoasterYmlConfigSourceProvider has been @deprecated. From this version onwards, the call public YamlConfigSource(String name, String source, int ordinal) is used instead.

4.6.1.3. Migration

The changes do not require any migration work and are backwards compatible

4.6.1.4. roaster-tm4j
  • The MissingResourceException has been fixed.

4.6.1.5. Migration

The changes do not require any migration work and are backwards compatible

4.7. 0.7.0 → 0.8.0

4.7.1. What’s new

A new module has been created: roaster-jaxrs. Its main objective is to provide solutions using JAX-RS specifications for needs that roaster-restassured cannot meet. For example, it handles multipart handling and leverages the capabilities of microprofile-rest-client, which can fulfill many customization needs. The roaster-jaxrs module has been accompanied by documentation titled Jaxrs and a testsuite.

4.7.2. Changes

4.7.2.1. General
  • The hu.icellmobilsoft.roaster.common.util.FileUtil has been enhanced with InputStream reading parameters, and it handles error management within the function itself.

BOM version upgrades
  • coffee v1.8.0 → v1.10.2

  • deltaspike 1.9.1 → 1.9.6

4.7.2.2. roaster-restassured
  • The HealthCheckTestHelper has been modified to support typed probe calls in addition to the general /health endpoint call.

    • /health/ready

    • /health/live

    • /health/started

  • The new roaster-jaxrs module has been integrated as a dependency due to shared configuration and usage (common classes). This causes minor changes in the Java path access.

4.7.2.3. Migration

The change requires minor adjustments, specifically the following Java paths have been updated:

  • hu.icellmobilsoft.roaster.restassured.response.producer.ManagedResponseProcessorConfig → hu.icellmobilsoft.roaster.jaxrs.response.producer.ManagedResponseProcessorConfig

  • hu.icellmobilsoft.roaster.restassured.response.producer.ResponseProcessorConfig → hu.icellmobilsoft.roaster.jaxrs.response.producer.ResponseProcessorConfig

  • hu.icellmobilsoft.roaster.restassured.response.producer.RestProcessor → hu.icellmobilsoft.roaster.jaxrs.response.producer.RestProcessor

Otherwise, there are no operational changes; everything else remains unchanged and backwards compatible.

4.7.2.4. roaster-redis

The update to Coff:ee caused a change in the hu.icellmobilsoft.roaster.redis.RedisHandler class (RedisService was deprecated in Coffee). The migration caused minor changes where method return types are now Optional instead of the previous String or void.

4.7.2.5. Migration

The change may require minor adjustments to return value types, otherwise it remains backward compatible.

4.8. 0.8.0 → 0.9.0

4.8.1. Changes

4.8.1.1. General
BOM version upgrades
  • jackson-datatype-jsr310 2.13.3 → 2.13.4

  • jackson-jaxrs-json-provider 2.13.3 → 2.13.4

  • jackson-annotations 2.13.3 → 2.13.4

  • jackson-databind 2.13.3 → 2.13.4.2

  • commons-text 1.8 → 1.10.0

  • coffee v1.10.2 → v1.12.1

4.8.1.2. Migration

The changes do not require any migration work and are backwards compatible

4.8.1.3. roaster-jaxrs
  • Added jakarta.servlet:jakarta.servlet-api to the dependency. This is a mandatory API, used for example by Resteasy dependencies. In its absence, the tests were terminated with an uninformative error message.

4.8.1.4. Migration

The changes do not require any migration work and are backwards compatible

4.9. 0.9.0 → 0.10.0

4.9.1. Changes

4.9.1.1. roaster-tm4j
  • The ability to specify different TM4J test cycles per test tag is now available using the roaster.tm4j.cycle.{0} configuration key

  • The exception related to the joint usage with the roaster-restassured module has been fixed

4.9.1.2. Migration

The changes do not require any migration work and are backwards compatible

4.9.1.3. JDK 17 support

The project now supports Java 17, making it usable in projects based on this Java version The problematic parts have been fixed in most places through dependency updates and expanded javadocs

java 11 && 17

Roaster will continue to be compiled with Java 11, but it will receive compilation and testing checks for Java 17 as well This will help minimize Java version compatibility issues

BOM version upgrades:
  • org.jboss.weld:weld-junit5 2.0.1.Final → 2.0.2.Final

  • org.jboss.weld:weld-core-impl 3.1.5.Final → 3.1.9.Final

  • io.smallrye.config 2.0.2 → 2.13.0

  • com.google.guava:guava 30.1.1-jre → 31.1-jre (The currently used Selenide version is no longer compatible with the old one)

4.10. 0.10.0 → 0.11.0

4.10.1. Changes

4.10.1.1. roaster-zephyr
  • The Roaster Zephyr module has been released. With its help, when using cloud-based Jira, you can display the test execution results of Zephyr Scale on the interface. To use it, the following changes need to be made:

    1. In the pom.xml, you need to replace the roaster-tm4j-junit5 module with the roaster-zephyr-junit5 module.

    2. In the test classes, you need to replace the extension with ZephyrExtension, and use the @TestCaseId annotation from the zephyr package in your tests.

    3. In the roaster yml files, you need to switch the TM4J configuration to the Zephyr configuration.

      1. You need to generate API keys in Cloud Jira for the user account that uploads tests, for both Jira and Zephyr.

      2. You should set the server/authToken value to the key obtained from the base64(email + ":" + jiraApiToken) operation.

        1. Alternatively, you can separately provide the server/email and server/apiToken keys with the Jira user email and the generated API token respectively. In this case, you should not provide the authToken.

      3. The value of cloud/bearerToken should be set to the JWT token generated in the Zephyr Scale Cloud interface.

4.11. → 2.0.0

4.11.1. Version upgrades

rest-assured: 3.3.0 → 5.0.0 (because of jakarta dto xml marshall/unmarshall)

4.11.2. WARNING

4.11.2.1. roaster-hibernate

Hibernate 6 brings changes to configurations in the hu.icellmobilsoft.roaster.hibernate.producer.EntityManagerFactoryProducer class. Something was already described by default, but the following two need additional attention:

...
        props.put(Environment.CDI_BEAN_MANAGER, beanManager); (1)

...
        props.put(Environment.USE_NEW_ID_GENERATOR_MAPPINGS, false); (2)
...
1 It probably received native support (https://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#beans-cdi)
2 It has been discontinued; it’s unclear what needs to be done…​.

4.11.3. BUG

4.11.3.1. roaster-testsuite-tm4j

Because of some problem (Dependency, Jakarta upgrade) these tests

  • hu.icellmobilsoft.roaster.testsuite.tm4j.Tm4jIT

  • hu.icellmobilsoft.roaster.testsuite.tm4j.Tm4jTagCycleIT

have been disabled until someone fixes the issue:

2022-12-27 10:19:32 5.14.0 WARNING Failed to invoke TestWatcher [hu.icellmobilsoft.roaster.tm4j.junit5.Tm4jExtension] for method [hu.icellmobilsoft.roaster.testsuite.tm4j.Tm4jIT#dummyTest()] with display name [dummyTest()]
java.lang.IllegalStateException: RestClientProxy is closed
	at org.jboss.resteasy.microprofile.client.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:98)
	at jdk.proxy2/jdk.proxy2.$Proxy95.headTestCase(Unknown Source)
	at hu.icellmobilsoft.roaster.tm4j.common.client.RestTm4jService.isTestCaseExist(RestTm4jService.java:96)
	at hu.icellmobilsoft.roaster.tm4j.common.RestTm4jReporter.lambda$4(RestTm4jReporter.java:122)
	...

4.12. 2.0.0 → 2.1.0

4.12.1. Changes

4.12.1.1. General
BOM version upgrades:
4.12.1.2. Hibernate
  • The configuration values related to logging and statistics generation that are burned in the EntityManagerFactoryProducer class are now loaded from the roaster configuration file: LOG_SESSION_METRICS, LOG_JDBC_WARNINGS, GENERATE_STATISTICS.

4.12.1.3. Migration
  • The configuration keys LOG_SESSION_METRICS, LOG_JDBC_WARNINGS, and GENERATE_STATISTICS are set to false instead of the previous default true if they are not included in the configuration file. Therefore, if we want to enable these, it is necessary to add them to the configuration file and set them to true.

4.12.1.4. roaster-tm4j/roaster-testsuite-tm4j

Bug fix due to Jakarta, dependency upgrade

  • hu.icellmobilsoft.roaster.testsuite.tm4j.Tm4jIT

  • hu.icellmobilsoft.roaster.testsuite.tm4j.Tm4jTagCycleIT

4.12.1.5. roaster-zephyr

Bug fix due to Jakarta, dependency upgrade

  • hu.icellmobilsoft.roaster.testsuite.zephyr.ZephyrIT

  • hu.icellmobilsoft.roaster.testsuite.zephyr.ZephyrTagCycleIT

4.12.2. roaster-selenide

"Due to an update in the Selenide version, the driverManagerEnabled switching option has been removed."

4.13. 2.1.0 → 2.2.0

4.13.1. Changes

4.13.1.1. General
  • projectName has been fixed to Roaster in license-maven-plugin

  • The @Vetoed annotations have been removed..

BOM version upgrades:

4.13.2. roaster-selenide

Accessing the clipboard content in Chrome browser, both during local and remote execution.

4.13.3. roaster-hibernate

In each persistence context, the EntityManagerFactory is created only once and remains cached as long as the @ApplicationScoped EntityManagerFactoryProducer instance lives in the container. During previous EntityManager injections, the database connection pool ran out because each EntityManagerFactory created its own connection pool.

4.14. 2.2.0 → 2.3.0

4.14.1. What’s new

A new module has been created: roaster-testdoc, which aims to generate AsciiDoc documentation from test classes. For more details, check the TestDoc chapter.

4.15. 2.3.0 → 2.4.0

4.15.1. What’s new

4.15.2. roaster-hibernate

The EntityManagerProducer now makes sure that only one EntityManager instance will be created per persistence unit, as opposed to creating a new EntityManager instance after each injection.

4.15.3. upgrade coffee version

Upgrade Coffee toolset version to 2.9.0

4.15.4. update release workflow

Update release workflow to use gh-action-mvn-release-main actions

4.16. 2.4.0 → 2.5.0

4.16.1. What’s new

4.16.1.1. roaster-zephyr

bugfix: The test reporting threw jakarta.json.bind.JsonbException: Json property self can not be mapped to a class class hu.icellmobilsoft.roaster.zephyr.dto.domain.jira.User exception

Migration

Changes are backwards compatible doesn’t need any migration.

4.17. 2.5.0 → 2.6.0

4.17.1. What’s new

4.17.1.1. roaster-jaxrs/roaster-estassured
  • The RestProcessor qualifier search is replaced in AbstractConfigurableResponseProcessorProducer.

Migration

Changes are backwards compatible doesn’t need any migration.