Recently I worked on setting up integration tests on our CircleCI CI/CD pipeline for Microsoft Teams Jira Connector Server and Data Center plugins.
Our product is a Java application that integrates Jira with Microsoft Teams to provide enhanced visibility in Microsoft Teams on projects in Jira.
In this article I’ll share how I did it.
TL; DR
If you are just interested in the specific technical details, here are the steps I followed:
- Create a Docker image that uses the CMD instruction to run atlas-run-standalone.
Docker file should look something like this:
FROM alpine
# Download Atlassian SDK...
CMD atlas-run-standalone --product jira --http-port 2990 --server 0.0.0.0 --jvmargs -Xmx4096M -DskipAllPrompts=true
2. Or you can use our alpine image.
Add that image as a secondary container in CircleCI config file:
docker:
- image: circleci/openjdk:8-jdk # Main image
- image: moveworkforward/atlas-run-standalone:jira-alpine # Secondary image
Build your Java application and run atlas-install-plugin to install it.
Wait for Atlassian application to be up and running and then run your integration tests.
What we tried to achieve
Although we had unit tests for both back-end and front-end, we did not have end to end tests running on our CI/CD pipeline.
We had integration tests but we had only been running them locally on each developer’s machine.
Our goal was to run Cypress and other integrations tests in CircleCI, to make sure that our code was still working fine after a change.
First attempt - Single container and atlas-run
First thing I tried was to run Atlassian Plugin SDK shell script atlas-run in the same way we run it locally in our machines and using just a single container.
CircleCI has the option to run steps in the background. When it’s set to true Job execution will immediately proceed to the next step rather than waiting for return of a command. That way you can leave servers running for example, while also executing the following steps.
- run:
name: Atlas Run
command: atlas-run
background: true
Unfortunately that didn't work. The application would start and then just stop right away without any error message.
So when the steps that had to run the tests were executed, the application was not longer running.
After contacting CircleCI support they mentioned it could be related to memory issues.
CircleCI has a resource_class feature that allows configuring CPU and RAM resources for each job.
At the time of writing this article, they offer the following options for running docker containers:
So I tried increasing the amount of RAM memory used by the container but that didn’t make any difference. Even with 16GB, which should be more than enough, the application would stop.
I also checked some of their articles related to Java Memory Errors but that didn’t help either.
Since I couldn’t see any error message, I needed a way to get more information about what was happening with that step.
CircleCI gives you the option to debug all your jobs with SSH. That way you can inspect things like log files, running processes, and directory paths.
I used it to try to run the application and check if I could get any useful information when it stopped.
Surprisingly, the application executed correctly while debugging with SSH. I could even SSH from another terminal and run the integration tests without any issue.
That made me think that maybe memory was not the real issue, so I decided to try something else.
Second attempt - Multiple containers and atlas-run-standalone
My second approach was to use another Atlassian script, atlas-run-standalone, which runs an Atlassian application standalone, without a plugin project. Also, I’d try to run it in a different docker container.
CircleCI gives you the option to specify multiple Docker images for your job.
The container created by the first image listed act as the primary container in which all steps are executed and you can use the other containers as services.
It works in a similar way as docker-compose, so all containers run in a common network and every exposed port will be available on localhost from a primary container.
You can use it to run different services like a Database, a shared cache like Redis, a message broker or any other software you need to run as a service.
Since I couldn’t find any official image for my secondary container that did exactly what I needed, I decided to create one.
I built moveworkforward/atlas-run-standalone. It’s based on Docker alpine and I installed Atlassian Plugin SDK and added atlas-run-standalone command.
Dockerfile looks something like this:
FROM alpine
# Download Atlassian SDK...
CMD atlas-run-standalone --product jira --http-port 2990 --server 0.0.0.0 --jvmargs -Xmx4096M -DskipAllPrompts=true
Then we needed a way to tell CircleCI to wait until our Atlassian application was up and running.
For that you can use tools like dockerize but I decided that curl and a simple bash script would do the trick. This is how that step looks like:
- run:
name: Wait for Jira
command: |
until $(curl --output /dev/null --silent --head --fail http://localhost:2990/jira);
do
printf '.'
sleep 5
done;
Once the application is running, we have to build our plugin and install it.
- run:
name: Build backend
command: atlas-package
- run:
name: Install Jira plugin
command: atlas-install-plugin --username username --password password --plugin-key com.your.plugin.key
You could also run your jobs in parallel with workflows to decrease your build times, which means building your application as a different job.
After all those steps finish successfully, we are ready to run our Cypress tests:
- run:
name: Cypress tests
command: npx cypress run
Depending on which image you choose as your primary container, you might need to install Cypress first.
If your tests start running and stop suddenly, you might be hitting your RAM memory limit.
As mentioned before, you can increase that with the resource_class feature. In my case, I had to set it to large.
build_and_test:
docker:
- image: circleci/openjdk:8-jdk
- image: moveworkforward/atlas-run-standalone:jira-alpine
resource_class: large
Summary
After following all those steps, we finally had our Cypress tests running in CircleCI.
This helps us be more confident after each code change since we can make sure our application is still working as expected.
Although in my case the application was a Jira plugin, I believe this approach can help you add integration tests to other applications that need some service running in the background.