Add unit test to Spring Boot

As a project grows testing becomes more vital. It’s easy to perform these test manually when you have just begun (I’m looking at myself) and repeat and enhance these as the code evolves.

But sooner or later unit test will add structure to your tests, save time and maybe uncover some bugs or other errors. I found one that is not an error as such but a result of how Spring Boot parses the last part of a rest-endpoint and it did lead to imprecise results. The reason is that when Spring Boot encounters a dot (.) it considers this a file-extension. Thanks to the good folks at Stackoverflow this thread have some solutions. I decided to implement the solution Dave Syer suggested.

I’m passing latitude and longitude in the rest-api as a decimal number and Spring Boot would initially interpret longitude with a value 6.3968833 as 6 because it discarded anything after the decimal point. And this move westward is not desired.

This post is a continuation of my rest-api post where I show how to query a database for cities/locations within a radius. The source code for this project can be found here.

Back to unit test. Spring Boot integrates nicely with JUnit, and JUnit integrates nicely with the Eclipse IDE so testing the code is easy.

Add a class to the project, I’ll call mine WorldcitiesApplicationTests, and put it in the src/test/java-folder. The first test will test for a non-existing endpoint.

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class WorldcitiesApplicationTests {

 @Autowired
 private MockMvc mvc;
 @Test
 public void getUnknowPath() throws Exception {
 mvc.perform(MockMvcRequestBuilders.get("/greetingtest").accept(MediaType.APPLICATION_JSON))
 .andExpect(status().isNotFound());
 }
}

To run the unit test mark the class and click on the green play button with the white triangle on the top of the IDE. At the bottom click at the JUnit tab to see result of the tests. Here I have all nine test passing.

springboot15

This first test is a nice start. But it doesn’t really test anything except it verifies that an endpoint does not exist. A useful test will return an array with zero or more items in it. Counting items in a json-response is described here and my first real test looks like this.

 @Test
 public void getEmptyCity() throws Exception {
 mvc.perform(MockMvcRequestBuilders.get("/city/a").accept(MediaType.APPLICATION_JSON))
 .andExpect(status().isOk())
 .andExpect(jsonPath("$.*", org.hamcrest.Matchers.hasSize(0)));
 }

hasSize() accepts an integer and here I want an empty array. Lets test for the nearest town called Sandane. In my case there is only one Sandane in the database, a city like Barcelona or Berlin will have more items in the array.

 @Test
 public void getOneCity() throws Exception {
 mvc.perform(MockMvcRequestBuilders.get("/city/Sandane").accept(MediaType.APPLICATION_JSON))
 .andExpect(status().isOk())
 .andExpect(jsonPath("$.*", org.hamcrest.Matchers.hasSize(1)));
 }

I define latitude and longitude with a default value and then parse them in a try-catch.

 Float lat = (float) 61.7428745;
 
 try {
 lat = Float.parseFloat(latitude);
 } catch (NumberFormatException e) { }

If latitude and longitude isn’t parsed into a float it retains the default value, which is where I live. So /citites/a/b will query locations within a radius of 20 km from my home. This test looks like this and will return 29 items in the array.

 @Test
 public void getCitiesNearBreimWithMalformedLatitudeAndLongitude() throws Exception {
 mvc.perform(MockMvcRequestBuilders.get("/cities/a/b").accept(MediaType.APPLICATION_JSON))
 .andExpect(status().isOk())
 .andExpect(jsonPath("$.*", org.hamcrest.Matchers.hasSize(29)));
 }

Next I wanted to test for the real location. Copied, pasted and modified the test and expected 29 items.

 @Test
 public void getCitiesNearBreimWithDefaultRadius() throws Exception {
 mvc.perform(MockMvcRequestBuilders.get("/cities/61.7428745/6.3968833").accept(MediaType.APPLICATION_JSON))
 .andExpect(status().isOk())
 .andExpect(jsonPath("$.*", org.hamcrest.Matchers.hasSize(29)));
 }

But this time I received 37 items and the test failed. Hmm, what is going on? I added a println() and saw longitude was changed from 6.3968833 to 6. As it turns out Spring Boot cuts of the decimal part as explained above. Adding this class to the projects modifies the parsing so I get to keep the decimal part.

@Configuration
public class AllResources extends WebMvcConfigurerAdapter {

 @Override
 public void configurePathMatch(PathMatchConfigurer matcher) {
 matcher.setUseRegisteredSuffixPatternMatch(true);
 }
}

After this I received 29 items and the test passed. If unit testing is added early in the process it quickly becomes a part of the coding routine. And it can uncover bugs or parsing peculiarities like this before it hits production.