Testing a player using mock events
Event Handling Tests
Using the Mock Service for developing and testing purposes
In this guide, only the usage and deployment of the mock service as well as its integration into your ci cd pipeline will be explained. For further information regarding the mock service itself, please refer to [this part](content/en/docs/Reference/Test/Mock Service) of the documentation.
The mock service is a testing microservice of our MSD ecosystem. Its purpose is to mock instances of the available events, the players can receive during a game play through, and certain event sequences via test scenarios. A player service can use the mock service to test its own event handling capabilities and its ability to perform certain necessary game playing functionalities like dynamically building the map and fighting other players.
You can use the mock service during the development stage and to test your player services event handling capabilities for each individual event without having to complete an entire game run through for it.
The following guide will show you how you an example using docker, maven and java spring.
Create event handling tests for your player
First, you need to create event handling tests (integration or system tests) for your player service.
The creation of these event handling tests is your personal responsibility and depends upon your choice of technology and test intention. The following is a simple example of a test, testing the players capability of handling a robot attacked event. It uses java spring and JUnit:
@Test
public void testRobotAttackedEventHandling() throws IOException {
// given
this.attackerId = UUID.randomUUID();
this.targetId = UUID.randomUUID();
this.attacker = new Robot(this.attackerId, 20, 50, true);
this.target = new Robot(this.targetId, 10, 35, true);
this.robotRepository.save(attacker);
this.robotRepository.save(target);
// helper method that creates a robot attacked event based on the two created robots and the defined damage, which
// is then later serialized into the request json string used in the rest call to the mock service
this.damageCaused = 5;
this.robotAttackedEvent = RobotAttackedEvent.fromRobotsAndDamageCaused(this.attacker, this.target, this.damageCaused);
String jsonRequest = objectMapper.writeValueAsString(this.robotAttackedEvent.toRequestDTO());
logger.info("Requested event (in JSON): " + jsonRequest);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity<String> postResponse = testRestTemplate.postForEntity(this.mockHost + "/robot/events/RobotAttacked",
new HttpEntity<>(jsonRequest, headers), String.class);
logger.info("Mock service response (in JSON): " + postResponse.getBody());
// when
// waiting for the player to receive and handle the requested robot attacked event
Thread.sleep(Duration.ofSeconds(5).toMillis());
// then
Robot attackerAfterCombat = this.robotRepository.findById(this.attackerId);
Robot targetAfterCombat = this.robotRepository.findById(this.targetId);
// checking whether the event has been handled correctly...
assertEquals(this.target.getHealth() - this.damageCaused, targetAfterCombat.getHealth());
assertTrue(targetAfterCombat.getAlive());
}
In this example, the involved entities are created and set according to the needs of the test. This involves two robots with a given health status and the damage that is caused by the attacker in this fight.
Based on these, a robot attacked event is created and then converted into a json string via the jackson object mapper.
This json request is then part of the rest call sent to the mock service, requesting a simple robot attacked event,
using the following
url: http://localhost:8098/robot/events/RobotAttacked
(more detail regarding the deployment of the mock service
is given later).
The generated event is then consumed by the player and passed to the corresponding internal event handler. The following assertions then verify whether the event handling was a success.
That would be a very basic mock test, testing whether the robot attacked event, published by the robot service, can be handled properly and causes the expected changes to the involved robot entities in the database. You should create a dedicated test for every single event that your player is expected to handle. To be noted here is that the format of the outputted events, of the mock service, is the same as that of the core services. You therefore can reformat that response into your own internal event domain object as you would the events of the core services.
There is a full set of example test code in the java spring skeleton player that you can use as a reference.
Running the mock service locally
Now that you have your tests, it is time to deploy the mock service.
The mock service image is available in the container registry of the MSD project group on gitlab. You can use the docker CLI to run the mock service locally via:
docker run -d -p 8098:8098 --name mock registry.gitlab.com/the-microservice-dungeon/devops-team/msd-image-registry/mock-service:latest
This command pulls the mock service image and then runs the mock service as a docker container on your machine. The mock service will be available under the port: 8098. You can adjust the port mapping according to your needs.
This is an example of how the mock service is run in the testing environment. The mock service itself needs other services available to be operational. Services such as RabbitMQ and Kafka/Radpanda also need to be deployed. There is a dedicated, docker-compose based, testing environment available in the MSD gitlab group in the dev-env section called “local-mock-environment”.
The local-mock-environment basically contains all the necessary services, including the mock service, and contains a docker-compose file via which you can deploy them on your machine. The commands for that are:
docker-compose pull | To pull tha images of the services
docker-compose up -d | To start the environment
docker-compose down -v | To stop and remove the environment and any volumes associated with it
Once the environment is up and running, you will be able to perform your tests. A guide on setting up and running the environment, the port mappings and everything else is available in the README file of the local-mock-environment.
Please remember that you will have to authenticate with the GitLab API first, before you can pull the mock service docker image or any other image from our container registry. You can do so using:
docker login registry.gitlab.com -u $USER -p $PAT
Please replace $USER with your gitlab username.
Please replace $PAT with a personal access token, that has the right of read_registry granted to it.
If you do not know how to generate a personal access token on gitlab, you can refer to the official gitlab documentation regarding personal access tokens.
Executing your tests
Once the mock service and the other services are running, you can start your tests. They will be able to access the mock service via rest.
To learn more about the rest api of the mock service, please refer to [this part](content/en/docs/Reference/Test/Mock Service/openapi.md) of the mock service documentation.
Integrate the mock service into your ci cd pipeline
Running the mock service locally can be very useful to test your implementation during the development stage of your player , but it is also highly recommended to automate the testing process.
Adding a event handling testing stage to your gitlab ci
Integrating the event handling tests into your ci cd pipeline is very recommended, as it will automatically perform the api tests after every committed change and inform you if your player is still in a ‘working’ state.
The following example shows, how such a stage could look like in your gitlab ci file:
:::info
The following example is configured for a player developed using maven and spring and utilizes the mock stage defined in or common ci-cd. If these technologies dont fit your player, you will have to make certain changes to your gitlab-ci file.
:::
variables:
# The test package containing your tests, separated by dots (e.g. com.example.player.test.integration).
# Make sure your test classes names end on Tests (e.g. MyPlayerIntegrationTests).
TEST_PACKAGE: "com.example.tests"
# Any maven image that fits your application (e.g. maven:3-openjdk-11 for java 11).
MAVEN_IMAGE: "docker.io/maven:3.6.3-openjdk-11"
# The identifier of your externally configurable kafka service address env variable. In spring applications usually defined in the application properties file.
KAFKA_BOOTSTRAP_ADDRESS: "KAFKA_BOOTSTRAP_ADDRESS"
# The identifier of your externally configurable rabbitmq host env variable. In spring applications usually defined in the application properties file.
RABBITMQ_HOST: "RABBITMQ_HOST"
# The identifier of your externally configurable mock service address env variable. In spring applications usually defined in the application properties file.
MOCK_SERVICE : "MOCK_SERVICE"
include:
- project: "the-microservice-dungeon/devops-team/common-ci-cd"
ref: "main"
file:
- "mock/mock-environment-test.yaml"
stages:
- mock
In this stage definition, your configuration file includes the global config file defined in the common ci-cd repository. During execution, it runs the local-mock-environment inside the gitlab runner using podman. Afterwards, a maven container is being run, in which the player is being deployed. The maven container is also connected to the internal container network.
The player, running inside the maven container, is now able to connect to the mock service using its
containers name as hostname. The fully qualified address, of the mock service, would
therefore be: mock:8098
If you are unsure about the individual components of the gitlab ci stage definition used here, you can check out the gitlab ci/cd documentation and the yaml syntax reference.
And that is it
Great, now that you have added the event test stage to your gitlab ci file, your player services event handling tests will be automatically executed every time you push new changes to your player repository, always informing you whether your player still passes your event handling tests :)