In this post, we’ll look at creating a simple Spring Boot Web application and generating tests for it using Diffblue Cover via the Intellij plugin. Our application will produce Fibonacci subsequences as a JSON array in a Web API.
The easiest way to get started with Spring Boot is to use the Spring Initializer to create a new project. At time of writing the defaults are to produce a Maven project, using Java 11 to produce a Jar based Spring Boot 2.3.3.
Typically, Spring Boot applications are web applications, so let’s include the “Spring Web” dependency too:
Load the project up in a copy of Intellij IDEA with the Diffblue Cover plugin installed and take a look around. Initially a DemoApplication has been created, along with a rudimentary DemoApplicationTest.
Best practice suggests that we should separate our application’s business logic from user interaction code, but it’s also common to start quick and dirty, then refactor later. We’ll take the latter approach, throwing together the bones of a web API, and then extract a business logic service later:
@RestController
public class FibonacciController {
@GetMapping("/fibonacci")
public List<Integer> fibonacci(
@RequestParam(required = false, defaultValue = "0") int start,
@RequestParam(required = false, defaultValue = "10") int count
) {
List<Integer> sequence = sequenceToNth(start + count);
return sequence.subList(start, sequence.size());
}
private List<Integer> sequenceToNth(int n) {
if (n < 0) throw new IllegalArgumentException();
List<Integer> sequence = new ArrayList<>(n);
int prev = 0, next = 1;
while(sequence.size() < n) {
sequence.add(next);
int sum = prev + next;
prev = next;
next = sum;
}
return sequence;
}
}
Traditionally, we’d spin up the application at this point and test it out by hand, making sure that it works once, which we can still do by running DemoApplication and browsing to http://localhost:8080/fibonacci.
The better thing to do at this point is to use Diffblue Cover to write tests for the newly produced code, which can later be used to detect regressions automatically:
We could also have tried “Write Tests” on the generated DemoApplication itself, but Diffblue Cover doesn’t consider main methods as testable so wouldn’t have produced anything. We can review the tests that were generated here:
@WebMvcTest(controllers = {FibonacciController.class})
@ExtendWith(SpringExtension.class)
public class FibonacciControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testFibonacci() throws Exception {
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders.get("/fibonacci");
ResultActions actualPerformResult = this.mockMvc.perform(requestBuilder);
ResultActions resultActions = actualPerformResult
.andExpect(MockMvcResultMatchers.status().isOk());
ResultActions resultActions1 = resultActions
.andExpect(MockMvcResultMatchers
.content()
.contentType("application/json"));
Matcher<String> matcher =
Matchers.containsString("[1,1,2,3,5,8,13,21,34,55]");
resultActions1.andExpect(MockMvcResultMatchers.content().string(matcher));
}
}
Cover has identified that this is a Spring component and needs testing with the SpringExtension; further, it’s identified we’re dealing with a Controller and so it’s appropriate to test using the MockMvc test framework.
Our application works now, so it’s time to tidy up and refactor our controller so that it delegates business logic to a service layer:
@RestController
public class FibonacciController {
@Autowired
private FibonacciService service;
@GetMapping("/fibonacci")
public List<Integer> fibonacci(
@RequestParam(required = false, defaultValue = "0") int start,
@RequestParam(required = false, defaultValue = "10") int count
) {
List<Integer> sequence = service.sequenceToNth(start + count);
return sequence.subList(start, sequence.size());
}
}
@Service
public class FibonacciService {
public List<Integer> sequenceToNth(int n) {
if (n < 0) throw new IllegalArgumentException();
List<Integer> sequence = new ArrayList<>(n);
int prev = 0, next = 1;
while(sequence.size() < n) {
sequence.add(next);
int sum = prev + next;
prev = next;
next = sum;
}
return sequence;
}
}
At this point, we can ask Cover to write tests again and we’ll get an updated controller test which uses a mocked FibonacciService:
@WebMvcTest(controllers = {FibonacciController.class})
@ExtendWith(SpringExtension.class)
public class FibonacciControllerTest {
@MockBean
private FibonacciService fibonacciService;
@Autowired
private MockMvc mockMvc;
@Test
public void testFibonacci() throws Exception {
when(this.fibonacciService.sequenceToNth(anyInt()))
.thenReturn(new ArrayList<Integer>());
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders.get("/fibonacci");
ResultActions actualPerformResult = this.mockMvc.perform(requestBuilder);
ResultActions resultActions = actualPerformResult
.andExpect(MockMvcResultMatchers.status().isOk());
ResultActions resultActions1 = resultActions
.andExpect(MockMvcResultMatchers.content().contentType("application/json"));
Matcher<String> matcher = Matchers.containsString("[]");
resultActions1.andExpect(MockMvcResultMatchers.content().string(matcher));
}
}
We also get a spring test for FibonacciService itself, including the exceptional case:
@ContextConfiguration(classes = {FibonacciService.class})
@ExtendWith(SpringExtension.class)
public class FibonacciServiceTest {
@Autowired
private FibonacciService fibonacciService;
@Test
public void testSequenceToNth() {
assertThrows(IllegalArgumentException.class, () ->
this.fibonacciService.sequenceToNth(-1));
}
@Test
public void testSequenceToNth2() {
List<Integer> actualSequenceToNthResult =
this.fibonacciService.sequenceToNth(1);
assertEquals(1, actualSequenceToNthResult.size());
assertEquals(1, actualSequenceToNthResult.get(0));
}
}
From here, it’s clear that testing Spring applications is pretty straightforward with Cover and avoids writing a lot of tests by hand! Try it out with our Community Edition; if you use IntelliJ, generate unit tests for free with our plugin that you can use with your open source Java projects.