Integration tests vs Unit tests
There are various types of testing which happens in the software development process. Unit testing, Integration testing, System testing etc.
Some use TDD, write the test cases first and then code and some write the test cases after code is done. Both the approaches have their advantage, though I am more favoured towards TDD as it ensures that you are writing modular and testable code from the beginning.
If you have never used TDD, or you are against it then please read the articles listed at the bottom of this article. Also, please post your opinions on those articles itself to get proper answers to your questions 🙂
Mostly, you will find that there are Unit test cases and Integration test cases. Some people get confused about these and in fact I have met some people who have the opinion that because we write the integration tests, unit tests are not required.
Let’s understand the difference between these two first.
Unit tests
- Should test only single unit
- Should be short and fast
As stated in the above points, Unit test is responsible for testing only single unit. It should never test the multiple layers or multiple classes. As it covers single unit it should be fast, as you are going to have lots of unit test cases.
Another point in unit tests is, you can assert the logic/data conversion or specific implementation that you use in your application. Important thing is to test the logic and not have test to just add the coverage.
E.g. let’s say you have row mapper in which you are converting db values into some Enum values depending on some condition.
public class ProfileRowMapper implements RowMapper<Profile> { @Override public Profile mapRow(ResultSet resultSet, int i) throws SQLException { Profile profile = new Profile(); profile.setType(ProfileType.valueOf(resultSet.getString("type"))); profile.setPicturePath(getPicturePath(resultSet)); return profile; } private String getPicturePath(ResultSet resultSet) { String picPath = resultSet.getString("pic_path"); return picPath != null ? picPath : DEFAULT_PATH; } }
Now following test case is adding the coverage but actually not testing the logic in the above code.
@RunWith(MockitoJUnitRunner.class) public class ProfileRowMapperTest { private final ProfileRowMapper profileRowMapper = new ProfileRowMapper(); @Mock private ResultSet resultSet; @Test public void testProfileMapping() throws SQLException { profileRowMapper.mapRow(resultSet, 1); verify(resultSet).getString("type"); verify(resultSet).getString("pic_path"); } }
Test cases like above should be avoided. Whereas following test case asserts the data conversion happening in the mapper. Note that this is one of the expected test cases. Set of various test cases should verify that conversion is working for all expected values.
@RunWith(MockitoJUnitRunner.class) public class ProfileRowMapperTest { private final ProfileRowMapper profileRowMapper = new ProfileRowMapper(); @Mock private ResultSet resultSet; @Test public void testProfileMapping() throws SQLException { given(resultSet.getString("type")).willReturn("2"); given(resultSet.getString("pic_path")).willReturn("some-pic-path"); Profile profile = profileRowMapper.mapRow(resultSet, 1); assertEquals(ProfileType.PREMIUM, profile.getType()); assertEquals("some-pic-path", profile.getPicturePath()); } }
Integration tests
- Should test integration between various layers
- Should not be there to cover some part of a unit
It doesn’t make sense to write an Integration test case for testing a unit. Now, you will say this is obvious but I have seen people writing integration test cases to cover small units. And then not writing Unit test cases as it is already covered in the integration test cases!
So the point is Integration test cases should test the integration between various units. Integration test cases will be slow by the nature, and that’s why integration test cases should be testing the integration and not the units.
Example of this would an integration test which provides input to a controller, in turn which will cover the flow till the point where data is fetched from database.
Consider following example of an integration test case
@Test void createNewProduct() throws Exception { mockMvc.perform(post("/products") .contentType(MediaType.APPLICATION_JSON) .content(copyToString(newProductJsonResource.getInputStream(), StandardCharsets.UTF_8)) ).andExpect(status().isOk()) .andExpect(jsonPath("$.id", isNonNull())); }
Demo source repository – https://github.com/sachingorade/demoserverapplication