This post was first published on my blog. Please read, Spring Boot Continuous Integration pipeline using CircleCI.
It is very important to automate the process of running the tests before deploying your code. CircleCI is a platform which can you to create CI pipelines for your project.
If you haven't yet set your test environment, you can do so by following my last post on Spring boot integration tests.
CircleCI will try to run the project according to the configuration specified in a special yml
file.
Login to CircleCI and turn on the test for the repository
You can easily log in to Circle CI using your GitHub or Bitbucket account. After that you can turn on the CI pipeline by clicking on Set up Project
button for that repository.
In the next step it will ask you about the language of the project. You can choose any and according to your choice it will create a branch with .circleci/config.yml
file in the root directory.
After a lot of tweaks to the config file that CircleCI created for me, I ended up with this.
.cirlceci file
version: 2.1
jobs:
build:
docker:
- image: circleci/openjdk:8-jdk
- image: circleci/mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: rootpw
MYSQL_DATABASE: test_db
MYSQL_USER: user
MYSQL_PASSWORD: passw0rd
steps:
- checkout
- run:
name: Waiting for MySQL to be ready
command: |
for i in `seq 1 10`;
do
nc -z 127.0.0.1 3306 && echo Success && exit 0
echo -n .
sleep 1
done
echo Failed waiting for MySQL && exit 1
- run:
name: Install MySQL CLI; Import dummy data; run an example query
command: |
sudo apt-get update
sudo apt-get install mysql*
mysql -h 127.0.0.1 -u root -prootpw --execute="use test_db;"
- run:
mvn test
Here we are using two docker images, Java 8
and MySQL
and according to the way circle CI is set up both the images are built on the separate containers. So, we have to wait for MySQL to start before running the project.
With - checkout
, we are getting the latest code from the given branch. Then we are waiting for the MySQL to be ready. Eventually, we are running a few SQL commands to connect to the database. Lastly, we are trying to run the tests.
Just push this part of the code and it will run the tests for you.
Test changes that I had to do.
The earlier written tests are good for local, and they work great when trying it on the local. But while running the tests on the CI, we have to test a little more.
Strategy for testing
In the single Test case I am going to test two APIs, one for creating the A School
and then checking if the entry was created or not.
Changes required in configuration
Since on CI, we have to create the tables again and again we have to change the following in the application-test.properties
file. Also, since we have changed the database name, we can change that configuration as well.
spring.datasource.url=jdbc:mysql://localhost:3306/test_db
spring.jpa.hibernate.ddl-auto=create-drop
Changes required in Test file.
According to the strategy change, we will change the way we write our test as well. This is how our updated test file looks like.
package io.singh1114.springboottut;
import org.json.JSONException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.*;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SchoolControllerTest {
@LocalServerPort
private int port;
TestRestTemplate restTemplate = new TestRestTemplate();
@Test
public void testGetSchoolData () throws JSONException {
HttpHeaders postHeaders = new HttpHeaders();
postHeaders.setContentType(MediaType.APPLICATION_JSON);
String requestJson = "{\"name\":\"School 1\", \"principle\":\"Mr. Charles\", \"address\":\"California\"}";
HttpEntity<String> schoolPostEntity = new HttpEntity<String>(requestJson, postHeaders);
restTemplate.exchange(
createURLWithPort("/school"),
HttpMethod.POST, schoolPostEntity, String.class);
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> entity = new HttpEntity<String>(null, headers);
ResponseEntity<String> response = restTemplate.exchange(
createURLWithPort("/schools"),
HttpMethod.GET, entity, String.class);
String expected = "[{\"id\":1,\"name\":\"School 1\",\"principle\":\"Mr. Charles\",\"address\":\"California\"}]";
JSONAssert.assertEquals(expected, response.getBody(), false);
}
private String createURLWithPort(String uri) {
return "http://localhost:" + port + uri;
}
}
Firstly, we are sending a POST
request to the /school
API to add data to the school database and finally we are sending a GET
request to get the data for /schools
API.
We can confirm that the things are working fine by running the tests in debug mode and stopping once the POST
request is made and checking in the database at the same time.
Once the test is done, you can run the same SQL command to see that the table was deleted.
Finally, you can push the code to see the tests running on CircleCI.
You can learn more about the changes in the following Pull Requests.
I hope you liked the post. Please share your views in the comment section below. Also, please Subscribe if you want to read more such posts.
Top comments (0)