[Java] Spring Boot 2 unit test writing memo

4 minute read

Introduction

Since I decided to write a unit test with spring boot2, I will organize the writing style.

How do you write a test of the value set in Model? In the case like this, I hope you can use it as a memory.

The sample code is given in GitHub.

Response Status test


import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

this.mockMvc.perform(get("/")).andDo(print())
        // Whether to return the status "200 OK"
        .andExpect(status().isOk())
        .andExpect(status().is(200))

The documentation looks like here.

Both isOk() and is(200) give the same result.

thymeleaf template file

@Controller
@EnableAutoConfiguration
public class HomeController {
  @GetMapping(path = "/")
  String home(HttpServletRequest request, Model model) {
    // specify home.html
    return "home";
  }
}

With that feeling, the thymeleaf template file is returned. This is a test.

    // test
    this.mockMvc.perform(get("/")).andDo(print())
        // Whether to return the template "home"
        .andExpect(view().name("home"))

Test with the name method. The document is here.

Model Testing

The controller fills the model with data, right?

public class MyData {
  private String strData;
  private int intData;

  public MyData(String strData, int intData) {
    this.strData = strData;
    this.intData = intData;
  }

  public void setStrData(String strData) {
    this.strData = strData;
  }
  public String getStrData() {
    return this.strData;
  }

  public void setIntData(int intData) {
    this.intData = intData;
  }
  public int getIntData() {
    return this.intData;
  }
}
@Controller
@EnableAutoConfiguration
public class HomeController {
  @GetMapping(path = "/")
  String home(HttpServletRequest request, Model model) {

    // string
    model.addAttribute("test", "this is test");

    // HashMap<String, String>
    HashMap<String, String> map = new HashMap<String, String>();
    map.put("name", "momotaro");
    map.put("age", "23");
    model.addAttribute("map", map);

    // List<String>
    List<String> list = new ArrayList<String>();
    list.add("list1");
    list.add("list2");
    list.add("list3");
    model.addAttribute("list", list);

    // List<MyData>
    List<MyData> list2 = new ArrayList<MyData>();
    list2.add(new MyData("test1", 111));
    list2.add(new MyData("test2", 222));
    model.addAttribute("list2", list2);

    // specify home.html
    return "home";
  }
}
@SpringBootTest(classes = HomeController.class)
@AutoConfigureMockMvc
public class HomeControllerTest {
  @Autowired
  private MockMvc mockMvc;

  @Test
  void home screen () throws Exception {

    HashMap<String, String> map = new HashMap<String, String>();
    map.put("name", "momotaro");
    map.put("age", "23");

    List<String> list = new ArrayList<String>();
    list.add("list1");
    list.add("list2");
    list.add("list3");

    List<MyData> list2 = new ArrayList<MyData>();
    list2.add(new MyData("test1", 111));
    list2.add(new MyData("test2", 222));


    // test
    this.mockMvc.perform(get("/")).andDo(print())
        // Whether to return the status "200 OK"
        .andExpect(status().isOk())
        .andExpect(status().is(200))

        // Whether to return the template "home"
        .andExpect(view().name("home"))

        // Model test.

        // string type
        .andExpect(model().attribute("test", "this is test"))

        // HashMap type
        .andExpect(model().attribute("map", map))

        // List<String> type
        .andExpect(model().attribute("list", hasSize(3))) // list size
        .andExpect(model().attribute("list", hasItem("list1"))) // is list1 included?
        .andExpect(model().attribute("list", hasItem("list2"))) // contains list2
        .andExpect(model().attribute("list", hasItem("list3"))) // contains list3
        .andExpect(model().attribute("list", contains("list1", "list2", "list3"))) // list1, list2, list3
        .andExpect(model().attribute("list", is(list))) // matches with list

        // List<MyData> type
        .andExpect(model().attribute("list2", hasSize(2))) // list size
        .andExpect(model().attribute("list2",
          hasItem(allOf(hasProperty("strData", is("test1")), hasProperty("intData", is(111))))
        )) // Is this combination of data included?
        .andExpect(model().attribute("list2",
          hasItem(allOf(hasProperty("strData", is("test2")), hasProperty("intData", is(222))))
        )) // Is this combination of data included?
        //.andExpect(model().attribute("list2", is(list2))) // Matches with list2 -> This cannot be written. I get an error.

        ;
  }
}

You can test with the attribute method like this: The document is here*Listcouldbedeterminedbyis(),butListcannotbedeterminedbyis().

redirect test

You do redirect in the POST process, right?

  @PostMapping(path = "/testpost")
  public ModelAndView testpost(RedirectAttributes redirectAttributes, @RequestParam(value = "intvalue", required = false, defaultValue = "0") Integer intvalue) {
    ModelAndView modelAndView = new ModelAndView("redirect:/testget");

    // check input value
    if (int value <= 0) {
      redirectAttributes.addFlashAttribute("error", "int value must be greater than 0");
      return modelAndView;
    }
    // set the data
    modelAndView.addObject("value", intvalue);
    return modelAndView;
  }
  @Test
  void testpost() throws Exception {
    // If you do not pass the parameter, it will be checked
    this.mockMvc.perform(post("/testpost")).andExpect(redirectedUrl("/testget"))
        .andExpect(flash().attribute("error", "int value must be greater than 0"));

    // Pass the parameter and pass the check
    this.mockMvc.perform(post("/testpost").param("intvalue", "5"))
        .andExpect(redirectedUrl("/testget?value=5"));
  }

Use redirectedUrl() to test the redirected URL.

Test redirectAttributes.addFlashAttribute() using flash().

The parameter received by POST can be specified with param().

test if a particular method was called with certain arguments

When the controller receives a POST I think I’ll do something. That is the test.

Here, DI of a specific Service is set in Controller, Let’s test that the method of Servcie is called in the Post process.

@Service
public class MyService {
  public void test(int value) {
    System.out.println("MyService.test()..." + value);
  }
}

@Controller
@EnableAutoConfiguration
public class HomeController {

  @Autowired
  MyService myService;

  @PostMapping(path = "/testpost2")
  public String testpost2(@RequestParam(value = "intvalue", required = false, defaultValue = "0") Integer intvalue) {

    // Call test() of MyService
    myService.test(intvalue);

    return "redirect:/";
  }
}
@SpringBootTest(classes = HomeController.class)
@AutoConfigureMockMvc
public class HomeControllerTest {
  @Autowired
  private MockMvc mockMvc;

  @MockBean
  private MyService myService;

  @Test
  void testpos2() throws Exception {
    // check redirectUrl
    this.mockMvc.perform(post("/testpost2").param("intvalue", "5")).andExpect(redirectedUrl("/"));

    // test if MyService's test() method was called with an argument value of 5
    verify(this.myService, times(1)).test(5);
  }
}

verify(this.myService, times(1)).test(5);

Is written, “The test method of the MyService class was called once with an argument value of 5” It will be a test.

The test whether it was called twice

verify(this.myService, times(2)).test(5);

And

If you want the argument value to be 10,

verify(this.myService, times(1)).test(10);

Is written.

by this, Controller testing Check the value of the parameter and divide the process, Passing parameter values to functions of other services I think that it can be narrowed down to two types.

Get parameter testing

The parameters of Post were specified by params(), The parameter of Get is specified by URL.

  @GetMapping(path = "/testget")
  String testget(@RequestParam(value = "value", required = false, defaultValue = "0") Integer value, @ModelAttribute("error") String error, Model model) {

    model.addAttribute("strError", error);
    model.addAttribute("intValue", value);

    // specify testget.html
    return "testget";
  }
  @Test
  void testget() throws Exception {
    this.mockMvc.perform(get("/testget?value=5&error=SuperError")).andDo(print())
        // Whether to return the status "200 OK"
        .andExpect(status().isOk())

        // Whether to return the template "testget"
        .andExpect(view().name("testget"))

        // Model test.
        .andExpect(model().attribute("intValue", 5))
        .andExpect(model().attribute("strError", "SuperError"))
        ;
  }

in conclusion

Since it is a memo at the time when I started writing the spring boot test, I would like to add it in the future.

The sample code is given in GitHub.

end.