Getting started
Quick Intro
Add Maven dependencies
CheetUnit artifacts are in the Maven central repository. In order to use CheetUnit you have to include two dependencies to your project.
Add the application server specific CheetUnit dependency to your pom. Currently, only wildfly is supported. This dependency publishes the CheetUnit insider http endpoint.
<dependency>
<groupId>io.github.cheetunit</groupId>
<artifactId>cheetunit-wildfly</artifactId>
<version>${cheetunit-version}</version>
</dependency>
Add the test specific CheetUnit dependency to your pom. This dependency provides functionality for sending the test class and the relating invoker class to the server side where it will be executed.
<dependency>
<groupId>io.github.cheetunit</groupId>
<artifactId>cheetunit-test</artifactId>
<version>${cheetunit-version}</version>
<scope>test</scope>
</dependency>
Enable CheetUnit on the server side
Set the system property cheetunit.enabled=true
on the server side. For example you can start your server with -Dcheetunit.enabled=true
Example for Wildfly / JBoss EAP
Set the system property in the standalone.xml:
<system-properties>
<property name="cheetunit.enabled" value="true"/>
</system-properties>
Set the system property via jboss-cli:
/system-property=cheetunit.enabled:add(value=true)
Don’t enable CheetUnit on production environment! Attackers could run code on the server side! We recommend to use a specific maven profile which includes the dependency.
Add configuration file
CheetUnit tests needs a property file which contains at least the following information:
-
cheetunit.host.protocol
= protocol of the CheetUnit insider endpoint, e.g. http -
cheetunit.host.name
= host name of server, e.g. localhost -
cheetunit.host.port
= port of the application, e.g. 8080 -
cheetunit.path
= path of the CheetUnit insider endpoint, e.g. hello/rest
The example values points to the following CheetUnit insider endpoint: http://localhost:8080/hello/rest/cheetunit-insider
.
The default path of the property file is src/test/resources/cheetunit.properties
Add Base Invoker class
You need to add an invoker class, which is transferred to the server and executes the method call on the server side. The HelloWorldService
in this example is the service that will be executed on the server.
public class HelloWorldServiceInvoker extends io.github.cheetunit.test.BaseServiceInvoker {
@Inject
private HelloWorldService service;
public String getHelloWorld() {
return service.getHelloWorld();
}
}
Write your test
class HelloWorldServiceIT {
private static HelloWorldServiceInvoker service;
@BeforeAll
static void beforeAll() {
service = CheetUnit.createProxy(HelloWorldServiceInvoker.class);
}
@Test
void getHelloWorld() {
String result = service.getHelloWorld();
Assertions.assertEquals("Hello World!", result);
}
}
Execute your test
- Deploy your application with the CheetUnit insider endpoint.
- Run your test
Implement your service class before you write the corresponding invoker class. If you prefer test-driven development, you can start implementing your service class with the signature of your method under the test. Keep in mind that you need to redeploy (or hot swap) after every change.
Configuration
The simplest way to configure CheetUnit is through a property file. The default path for the property file is src/test/resources/cheetunit.properties
. It is possible to have multiple property files which can be relevant for different configuration, e.g. continuous integration environments. If you want to use a specific property file which has not the default name, you can pass the system property cheetunit.property.file
with the regarding property file name to your process call. In addition, it is possible to override existing property definitions through the CLI.
All mandatory properties describe the path to the CheetUnit insider endpoint so that the client side of CheetUnit (Unit tests) knows where to transfer the invoker class.
-
cheetunit.host.protocol
=http -
cheetunit.host.name
=localhost -
cheetunit.host.port
=8080 -
cheetunit.path
=hello/rest
CheetUnit also supports a few optional properties which are very convenient for debugging or long-running tests.
-
cheetunit.httpclient.connecttimeout
=60000 -
cheetunit.httpclient.readtimeout
=60000 -
cheetunit.httpclient.writetimeout
=60000
The example values set the connect, read and write timeout of the CheetUnit's http client to 60000ms / 1 minute.
Use Cases/Examples
Full examples can be found here.
Hello World
CheetUnit can be used to test services, which are accessing a database (see Transactions below) and also for other services.
In the Hello World Example a simple method, which has no input-parameters, is tested. It returns a String which contains "Hello World!";
The Logger in the example proves, that the method is executed on the server side.
@ApplicationScoped
public class HelloWorldService {
private static final Logger LOG = LoggerFactory.getLogger(HelloWorldService.class);
public String getHelloWorld() {
LOG.info("Executing helloWorld service.");
return "Hello World!";
}
}
The associated Invoker-Class is HelloWorldServiceInvoker. This is the class which is transferred to the server and executes the HelloWorldService there.
public class HelloWorldServiceInvoker extends BaseServiceInvoker {
@Inject
private HelloWorldService service;
public String getHelloWorld() {
return service.getHelloWorld();
}
}
When the application is deployed on the server, with CheetUnit it is very simple to write an integration test.
class HelloWorldServiceIT {
private static HelloWorldServiceInvoker service;
@BeforeAll
static void beforeAll() {
service = CheetUnit.createProxy(HelloWorldServiceInvoker.class);
}
@Test
void getHelloWorld() {
String result = service.getHelloWorld();
Assertions.assertEquals("Hello World!", result);
}
}
Method Call with parameters
In addition, we provided some examples where we pass different kind of parameters (primitives, enum, pojos) and expect a return object. Check out our example test GreeterServiceIT.
Currently there is a restriction: you can’t use lambdas, other functional objects or instances of anonymous inner classes as parameters for CheetUnit calls. We plan to support this in future versions.
Transactions
By default, each CheetUnit call is executed in a separate transaction. This is required in some situations.
For example: your method call returns a JPA entity having fields that are lazy loaded. While the serialization of the entity, all fields are being loaded. This action requires an active transaction.
If your transactions are managed by a container, you do not need to do anything. However, if you manage transactions by yourself (e.g. via tx.begin();
, tx.commit();
), you need to execute CheetUnit calls without transaction. This can be done via the annotating of the corresponding invoker or an invoker method:
@CheetUnitNoTransactionRequired
public class PersonServiceInvoker extends BaseServiceInvoker {
@Inject
private PersonService service;
public List<Person> getAllPersons() {
List<Person> allPersons = service.getAllPersons();
return allPersons;
}
public Person getPersonById(Long id) {
return service.getPersonById(id);
}
}
See our example for more details.