Pages

Writing automated unit and integration test cases for Spring boot application and gradle | Approach | case example

I want to write about my experience of a TDD environment where I had a unique problem and how i solved it.

The problem was that i have to develop an application which provides some endpoints to another team to access our teams features. The application would receive some service request, call a bunch of other web services to do some analysis and then return the status of the request (valid, invalid with other details). I have to write unit tests as well as integration tests while developing the application, so that, in the future, if someone else made any code change and break something, the tests should fail. The application was being build and deployed to the servers using a continuous integration pipeline. The problem is that I cannot assume that other services that my service is depending on, are always up, because if they are down, the test would fail and the changes would not deploy in the development servers even if there is nothing wrong with my code.

Solution.
The solution to this kind of problem is to separate your tests to unit tests and integration tests. The unit tests should run every time you are trying to build the application and deploy it to the servers. There should be no external dependencies in the unit tests. For example, external services, external databases, message queues or streams should not be called in the unit test context. In the integration test, you test the integration among different components so they could be unit-integration test or integration among different applications/systems. In those automated tests, you could access the separate components and applications.

These concepts are much better described in the awesome article written by the very awesome Martin Fowler in his blog. Do check that out.

So for my approach to separate out the unit tests and the integration tests, I adopted the following approach.

First create categories on which you want to separate your tests into:

import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@WebMvcTest
public interface IntegrationTest {

}

public interface UnitTest {

}



Then every test case that you want to mark as UnitTest or integrationTest, you can simply apply the category.

import io.restassured.http.ContentType;
import org.codehaus.jettison.json.JSONObject;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import com.rajan.IntegrationTest;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static io.restassured.RestAssured.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

;

@RunWith(SpringRunner.class)
@WebMvcTest
@Category(IntegrationTest.class)
public class ControllerTest {

You will require Junit4 though to use this feature of junit.
Once the tests are categorized, you can write a gradle job to run instances of a particular category as follows.

// add this in the build.gradle
task integrationtest(type: Test){

 environment "context", "inttest"
 useJUnit{
  includeCategories 'com.rajan.IntegrationTest'
 }
}

// to execute this job from command do the following
gradle clean integrationtest

The above command will run all the instances of the tests market by the marker interface IntegrationTest class.

You will have to add the test dependencies in the build.gradle file to do these tasks.

//testCompile( 'com.github.tomakehurst:wiremock:2.6.0' )
 testCompile( 'io.rest-assured:rest-assured:3.0.2' )
 testCompile( 'org.springframework.boot:spring-boot-starter-test' )
 
 testCompile( 'org.powermock:powermock-module-junit4:1.6.2' )
 //testCompile( 'org.powermock:powermock-api-mockito:1.10.8' )
 testCompile( 'org.mockito:mockito-core:1.10.8' )
 testCompile( 'org.easymock:easymock:3.4' )
 testCompile group: 'org.hamcrest', name: 'hamcrest-core', version: '1.3'
        testCompile group: 'org.hamcrest', name: 'hamcrest-library', version: '1.3'

After this
I would also like to talk a little bit about a few popular libraries out there for writing tests and some popular features of them.

As you saw in the dependencies above, the first is wiremock. Wiremock is the api where you can record what responses to return when some webservice or http url is hit. You can call this as mocking any service call. It has its own limitations and I couldn't use it in my case but for simple cases it can be pretty useful.

Second is rest assured. Rest assured is an api where you can do a proper http call with headers and body, so its more a rest call api than a testing api.

Power mock and Mockito are mostly used to mock those parts of code which you don't want to actually run in your testing context. For example, you are writing a unit test for a method which does some logic but calls some external web-service and gets some results and then does some logic based on that. You don't want to actually call the external web-service when running the unit test. In cases like that, you can use mocking. For example, I created a mock RestTemplate and configured it to return a certain mocked response when a particular method is called. Then i set the RestTemplate to the actual object, whose method i am going to call. Now when i call the method in the test context, there is no actual service call, and the response object is returned. I assert the output of the method i am testing based on that particular response from the mocked web-service.

                // mock the response entity to return 400 status code
  ResponseEntity<ASNResponseDTO> response = mock(ResponseEntity.class);
  Mockito.when(response.getStatusCodeValue()).thenReturn(HttpStatus.BAD_REQUEST.value());

  // mock rest template to return the mocked response
  RestTemplate mockedRestTemplate = mock(RestTemplate.class);
  Mockito.when(mockedRestTemplate.exchange(Mockito.anyObject(), Mockito.eq(HttpMethod.GET), Mockito.anyObject(),
    Mockito.eq(ASNResponseDTO.class))).thenReturn(response);

  // set mocked restTemplate
  objectHandler.setProfileBasedConfig(new DevelopmentConfiguration());
  objectHandler.setRestTemplate(mockedRestTemplate);

In the spring, i am using setter based dependency injection so that its pretty straight forward to set a mocked object during testing.

Hope this helps somebody understand the concepts as well as the technology.

Please find some useful links below.
Powermock
Mockito
https://stackoverflow.com/questions/2606572/junit-splitting-integration-test-and-unit-tests
https://dzone.com/articles/unit-and-integration-tests
http://mrhaki.blogspot.com/2013/05/gradle-goodness-running-single-test.html

3 comments:

  1. That is very interesting; you are a very skilled blogger. I have shared your website in my social networks! A very nice guide. I will definitely follow these tips. Thank you for sharing such detailed article.

    selenium training in bangalore|

    ReplyDelete
  2. Your post is really very helpful. Easy to learn as you explain things precisely. Thank you so much.
    web design company in velachery

    ReplyDelete

If you like to say anything (good/bad), Please do not hesitate...