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:
Then every test case that you want to mark as UnitTest or integrationTest, you can simply apply the category.
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.
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.
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.
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.