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:
<profiles>
<profile>
<id>ci</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<modules>
<module>project-testsuite</module>
</modules>
</profile>
</profiles>
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.
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:
roaster:
test:
sample:
url: http://roaster-default.hu
port: 8083
pass: secret
roaster:
test:
sample:
url: http://localhost
port: 8084
roaster:
test:
sample:
url: http://roaster-sandbox.hu
@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;
~/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:
|
3. Modules:
3.1. API
To use the API module, you just need to reference it in the pom.xml file
<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.
<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.
-
The response HTTP status is 200 and "application/json" is accepted.
-
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.
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;
}
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.
<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.
<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:
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
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.
@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)
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
<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:
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
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.
@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)
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);
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
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:
<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
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:
[Manage Api Tokens for your Atlassian Account](https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/)
[Generating API Access Tokens (Bearer Token)](https://support.smartbear.com/zephyr-scale-cloud/docs/rest-api/generating-api-access-tokens.html)
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:
<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.
<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:
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
# 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
@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:
<dependency>
<groupId>hu.icellmobilsoft.roaster</groupId>
<artifactId>roaster-selenide-angular</artifactId>
</dependency>
Currently supported components:
3.8.3.1. <mat-checkbox>
MatCheckbox checkbox = new MatCheckbox(Selenide.$(MatCheckbox.TAG_NAME));
assertFalse(checkbox.getValue());
checkbox.toggle();
assertTrue(checkbox.getValue());
3.8.3.2. <mat-radio-group>
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>
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>
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:
<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:
<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>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
<!-- 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:
<?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:
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
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
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
<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:
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:
<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
:
<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 ofuserName
to load theExecuted by
value
4.3.1.3. roaster-restassured
-
The ResponseProcessor has been enhanced with the
putXml
andputJson
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 anexpectedOpenapiVersion
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
anddecorateValidatableResponse
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 against3.0.3
OpenAPI version instead of3.0.1
. If this does not meet the project requirements, theOpenAPITestHelper.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
anddeleteJson
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
-
The Selenide upgrade may cause issues. See: Selenide changelog
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 inRoasterYmlConfigSourceProvider
has been @deprecated. From this version onwards, the callpublic 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 withInputStream
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
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:
-
In the pom.xml, you need to replace the roaster-tm4j-junit5 module with the roaster-zephyr-junit5 module.
-
In the test classes, you need to replace the extension with ZephyrExtension, and use the @TestCaseId annotation from the zephyr package in your tests.
-
In the roaster yml files, you need to switch the TM4J configuration to the Zephyr configuration.
-
You need to generate API keys in Cloud Jira for the user account that uploads tests, for both Jira and Zephyr.
-
You should set the
server/authToken
value to the key obtained from the base64(email + ":" + jiraApiToken) operation.-
Alternatively, you can separately provide the
server/email
andserver/apiToken
keys with the Jira user email and the generated API token respectively. In this case, you should not provide theauthToken
.
-
-
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:
-
Bump com.google.guava:guava 31.1-jre → 32.1.1-jre (CVE-2023-2976)
-
com.codeborne:selenide: 6.10.3 → 6.18.0
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
, andGENERATE_STATISTICS
are set tofalse
instead of the previous defaulttrue
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 totrue
.
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:
-
com.codeborne:selenide: 6.18.0 → 6.19.1
-
hu.icellmobilsoft.coffee:coffee: 2.3.0 → 2.7.0 , See necessary changes:
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 inAbstractConfigurableResponseProcessorProducer
.
Migration
Changes are backwards compatible doesn’t need any migration.