5 min Read.
Hola !
This post is for brushing up our skills on Testing framework, first we will scratch the surface of testing by understanding the basic differences between Unit and Integration. Followed by examples on both, we will also look at the annotations which come handy while writing test cases. Code base link
Unit Testing
Unit testing as the name suggests here we test the individual part of our software. Unit Testing is carried out ideally during the development phase of SDLC. Individual parts can be individual function/method or procedure. Unit Testing is done typically by developer. This process helps the developer to identify any missed functionality in a method/function.
Integration Testing
Integration Testing is process of testing interfaces between two or more modules. The purpose of Integration testing is to check the correctness of interaction between two or more interfaces. In Integration Testing modules can be different layers in our code or can be entirely different services. Ideally Integration Testing is always done after Unit Testing.
Bulleted Differences :
Unit Testing | Integration Testing |
Each module is tested separately | All modules are tested combined |
Basically done by Developer who is aware of internal design also known as White Box Testing |
Done by Tester, who is not aware of internal design also known as Black Box Testing |
Performed first step of all testing processes | Done after Unit testing and before System Testing |
Detection of defects is easy | Detection of defects is difficult |
Less Cost | High Cost |
Testing Flow :
Example of Unit Testing :
Code Snippet 1
1 2 3 4 5 |
public class MyClass { public int multiply(int i, int j) { return i*j; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import static org.junit.Assert.*; import org.junit.Test; public class MyTests { @Test public void multiplicationOfZeroIntegersShouldReturnZero() { MyClass tester = new MyClass(); // MyClass is tested // assert statements assertEquals(0, tester.multiply(10, 0)); // "10 x 0 must be 0" assertEquals(0, tester.multiply(0, 10)); // "0 x 10 must be 0" assertEquals(0, tester.multiply(0, 0)); // "0 x 0 must be 0" } } |
In Code snippet 1, we have a class which contains the method multiply, we will use assertEquals(int expected, int actual) which is pretty basic stuff for experienced developers. Let’s have a look at unit test cases in real scenarios using Mockito in our Spring boot application, used in our day to day development
Example of Integration Testing :
Code Snippet 2 : Testing MVC/Controller Layer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
package com.springtest.controllertests; import static org.hamcrest.Matchers.hasSize; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.ArrayList; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import static org.mockito.Matchers.anyString; import com.springtest.controller.StudentController; import com.springtest.dto.StudentDTO; import com.springtest.service.StudentService; @RunWith(SpringRunner.class) @WebMvcTest(StudentController.class) public class ControllerTests{ @Autowired private MockMvc mockMVC; @MockBean private StudentService studentService; @Test public void test_getAllStudents_endpoint() throws Exception{ List<StudentDTO> studentList = new ArrayList<StudentDTO>(); StudentDTO stu1 = new StudentDTO(); studentList.add(stu1); studentList.add(stu1); studentList.add(stu1); when(studentService.getStudentList()).thenReturn(studentList); mockMVC.perform(get("/api/v1/students/getAllStudents")) .andDo(print()) .andExpect(status().isOk()) .andExpect(jsonPath("$",hasSize(3) )); } @Test public void test_searchStudent_endpoint() throws Exception { when(studentService.searchStudent("Lokesh")).thenReturn(new StudentDTO("Lokesh","X",79)); mockMVC.perform(get("/api/v1/students/findStudent/{name}","Lokesh")) .andDo(print()) .andExpect(status().isOk()) .andExpect(jsonPath("$.studentName").value("Lokesh")); } @Test public void test_searchStudent_endpoint_with_anyIput() throws Exception { when(studentService.searchStudent(anyString())).thenReturn(new StudentDTO("Suresh","XI",56)); mockMVC.perform(get("/api/v1/students/findStudent/{name}","Lokesh")) .andDo(print()) .andExpect(status().isOk()) .andExpect(jsonPath("$.studentName").value("Lokesh")); } } |
Above code snippet 2 is for testing Spring Controller layer, we can observe few annotations and methods, please read on for more description:
@WebMvcTest(StudentController.class) used for mocking the HTTP methods present in StudentController.class
@RunWith(SpringRunner.class) SpringRunner is an alias for SpringJUnit4ClassRunner which joins the JUnit with Spring TestContext framework, with SpringRunner we can implement simple JUnit based Unit and Integration Tests
andDo(), allows us to perform general operation like print the request and response bodies
andExpect(), allows us to perform testing the expected outcomes
anyString(), used for mocking the behaviour of any argument for the given type here we are using anyString(), we also have anyInt(), any(Object.class) as viable options
Code Snippet 3 : Integration Test for Spring Boot Application
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
package com.springtest.serviceTests; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.springtest.dao.StudentDao; import com.springtest.dto.StudentDTO; import com.springtest.entity.StudentEntity; import com.springtest.service.StudentService; import static org.junit.Assert.*; @SpringBootTest @RunWith(SpringRunner.class) public class ServiceTests { @Autowired private StudentService studentService; @Autowired private StudentDao studentDao; @Test public void getStudentList_tests() { List<StudentDTO> studentList = studentService.getStudentList(); assertEquals(studentList.size(), 3); } @Test public void searchStudent_tests(){ StudentEntity studentEntity = studentDao.findByStudentName("Lokesh Gupta"); assertEquals(studentEntity.getStudentName(), "Lokesh Gupta"); } } |
In above code snippet 3 : we can observe a new annotation @SpringBootTest, this annotation allows us to spin up the whole application, creating real objects, connecting to real database(can be InMemory database) or calling other services or connecting to third Party APIs, in other words trying to startup the application just for testing purposes. It is also known as Black Box Testing we don’t mock any thing in this type of testing every object is real.
Big question when we should we use @RunWith(SpringRunner.class) and @SpringBootTest annotations or both?
When we want to test a method under our Spring Boot Application mocking other objects of application like database connection/transactions or 3rd Party APIs we use @RunWith(SpringRunner.class) or @RunWith(MockitoJUnitRunner.class) and when we want to connect to real API or databases based on the real configurations we use @SpringBootTest annotations.
Thanks for reading this blog, I hope it was useful !
For more detailed information please reach out to me at atish.verma@imaginea.com