Write unit tests in the Spring Boot

Write unit tests can help developers write high-quality code, improve code quality and reduce the Bug, to facilitate reconstruction. Spring Boot provides a number of utilities and annotations to help us test the application, open the unit tests in the Spring Boot in just introduced spring-boot-starter-test can be, it contains a number of mainstream test library. In this paper, based on the Service Controller and unit testing.

Introducing Spring-Boot-Starter-Test :

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

 

Run Maven command dependency: tree can be seen that contains the following dependence:

[INFO] +- org.springframework.boot:spring-boot-starter-test:jar:1.5.9.RELEASE:test
[INFO] | +- org.springframework.boot:spring-boot-test:jar:1.5.9.RELEASE:test
[INFO] | +- org.springframework.boot:spring-boot-test-autoconfigure:jar:1.5.9.RELEASE:test
[INFO] | +- com.jayway.jsonpath:json-path:jar:2.2.0:test
[INFO] | | +- net.minidev:json-smart:jar:2.2.1:test
[INFO] | | | \- net.minidev:accessors-smart:jar:1.1:test
[INFO] | | |     \- org.ow2.asm:asm:jar:5.0.3:test
[INFO] | | \- org.slf4j:slf4j-api:jar:1.7.25:compile
[INFO] | +- junit:junit:jar:4.12:test
[INFO] | +- org.assertj:assertj-core:jar:2.6.0:test
[INFO] | +- org.mockito:mockito-core:jar:1.10.19:test
[INFO] | | \- org.objenesis:objenesis:jar:2.1:test
[INFO] | +- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] | +- org.hamcrest:hamcrest-library:jar:1.3:test
[INFO] | +- org.skyscreamer:jsonassert:jar:1.4.0:test
[INFO] | | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] | +- org.springframework:spring-core:jar:4.3.13.RELEASE:compile
[INFO] | \- org.springframework:spring-test:jar:4.3.13.RELEASE:test

 

  • JUnit, unit testing standard Java application;

  • Spring Test & Spring Boot Test, the test unit to provide support for Spring Boot application;

  • Mockito, Java mocking framework to simulate any Spring-managed Bean, analog data such as a third-party system Service interface returned in the unit test, and not to actually call a third-party system;

  • AssertJ, a smooth assertion library, but also provides a way to compare more expectations and test the return value;

  • Hamcrest, matching object library (also referred to as a predicate or constraint);

  • JsonPath, providing XPath as JSON symbols to obtain similar data segments;

  • JSONassert, JSON string of JSON objects or assertion libraries.

A standard test unit Spring Boot code should have the following structure:

import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTest {

}

 

Intellectual preparation

JUnit4 comment

JUnit4 contains several important notes: @BeforeClass , @AfterClass , @Before , @After and @Test . Which, @BeforeClass and @AfterClass run and each class is loaded beginning of the end, it must be static; and @Before and @After run before and after each test method to start over. See the following example:

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestApplicationTests {

  @BeforeClass
  public static void beforeClassTest() {
      System.out.println("before class test");
  }
   
  @Before
  public void beforeTest() {
      System.out.println("before test");
  }
   
  @Test
  public void Test1() {
      System.out.println("test 1+1=2");
      Assert.assertEquals(2, 1 + 1);
  }
   
  @Test
  public void Test2() {
      System.out.println("test 2+2=4");
      Assert.assertEquals(4, 2 + 2);
  }
   
  @After
  public void afterTest() {
      System.out.println("after test");
  }
   
  @AfterClass
  public static void afterClassTest() {
      System.out.println("after class test");
  }
}

 

Run the following output:

...
before class test
before test
test 1+1=2
after test
before test
test 2+2=4
after test
after class test
...

 

As can be seen from the above output operation timing of each annotation.

Assert

In the above code, we use assert port Assert class provides methods, the following are some of the common methods assert:

  • the assertEquals ( "Message", A, B) , the object A is determined and the object B are equal, the call is determined when comparing two objects equals () methods.

  • assertSame ( "Message", A, B) , and determines whether the object is the same as A B object, using == operator.

  • assertTrue ( "Message", A) , A is determined whether a condition is true.

  • assertFalse ( "Message", A) , A is determined whether or not the condition is true.

  • assertNotNull ( "Message", A) , A is determined as the object is not null .

  • assertArrayEquals ( "Message", A, B) , determines whether the array A array and B are equal.

MockMvc

Hereinafter, the test of Controller of the need to use MockMvc technology. MockMvc, means literally MVC is analog, i.e., it can simulate an MVC environment, and then sends a request to get the response Controller.

In the test unit, it needs to be initialized before use MockMvc, as follows:

private MockMvc mockMvc;

@Autowired
private WebApplicationContext wac;

@Before
public void setupMockMvc(){
  mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}

 

MockMvc simulation MVC request

Simulation of a get request:

mockMvc.perform(MockMvcRequestBuilders.get("/hello?name={name}","mrbird"));

 

Simulation of a post request:

mockMvc.perform(MockMvcRequestBuilders.post("/user/{id}", 1));

 

Simulation file upload:

mockMvc.perform(MockMvcRequestBuilders.fileUpload("/fileupload").file("file", "文件内容".getBytes("utf-8")));

 

Analog request parameters:

// analog transmit a message parameter value Hello 
mockMvc.perform (MockMvcRequestBuilders.get ( "/ Hello") param ( "message", "Hello").);
// submit a checkbox analog value, name of Hobby, value of sleep and EAT
mockMvc.perform (MockMvcRequestBuilders.get ( "/ saveHobby") param ( "Hobby", "sleep", "EAT").);

 

Can also be used directly MultiValueMap build parameters:

MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("name", "mrbird");
params.add("hobby", "sleep");
params.add("hobby", "eat");
mockMvc.perform(MockMvcRequestBuilders.get("/hobby/save").params(params));

 

JSON analog transmission parameters:

String jsonStr = "{\"username\":\"Dopa\",\"passwd\":\"ac3af72d9f95161a502fd326865c2f15\",\"status\":\"1\"}";
mockMvc.perform(MockMvcRequestBuilders.post("/user/save").content(jsonStr.getBytes()));

 

In the actual test, to manually prepare such a long string JSON format is tedious and very error prone, you can use Spring Boot Jackson comes techniques to serialize a Java object (refer JSON Spring Boot in the art ), as shown below :

User user = new User();
user.setUsername("Dopa");
user.setPasswd("ac3af72d9f95161a502fd326865c2f15");
user.setStatus("1");

String userJson = mapper.writeValueAsString(user);
mockMvc.perform(MockMvcRequestBuilders.post("/user/save").content(userJson.getBytes()));

 

Which, mapper is com.fasterxml.jackson.databind.ObjectMapper object.

Analog Session and Cookie:

mockMvc.perform(MockMvcRequestBuilders.get("/index").sessionAttr(name, value));
mockMvc.perform(MockMvcRequestBuilders.get("/index").cookie(new Cookie(name, value)));

 

Content-Type request is provided:

mockMvc.perform(MockMvcRequestBuilders.get("/index").contentType(MediaType.APPLICATION_JSON_UTF8));

 

Format is set to return JSON:

mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", 1).accept(MediaType.APPLICATION_JSON));

 

Analog HTTP request header:

mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", 1).header(name, values));

 

MockMvc processing returns results

We expect a successful call, that HTTP Status 200:

mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", 1))
  .andExpect(MockMvcResultMatchers.status().isOk());

 

It is expected to return the contents of the Application / json :

mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", 1))
  .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON));

 

Check the return value of the contents of a JSON data:

mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", 1))
  .andExpect(MockMvcResultMatchers.jsonPath("$.username").value("mrbird"));

 

Used here to jsonPath , $ represent JSON root node. More about jsonPath presented refer https://github.com/json-path/JsonPath .

Analyzing method returns a view Controller:

mockMvc.perform(MockMvcRequestBuilders.post("/index"))
  .andExpect(MockMvcResultMatchers.view().name("index.html"));

 

Compare Model:

mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", 1))
  .andExpect(MockMvcResultMatchers.model().size(1))
  .andExpect(MockMvcResultMatchers.model().attributeExists("password"))
  .andExpect(MockMvcResultMatchers.model().attribute("username", "mrbird"));

 

Compare forward or redirect:

mockMvc.perform(MockMvcRequestBuilders.get("/index"))
  .andExpect(MockMvcResultMatchers.forwardedUrl("index.html"));
// 或者
mockMvc.perform(MockMvcRequestBuilders.get("/index"))
  .andExpect(MockMvcResultMatchers.redirectedUrl("index.html"));

 

Compare Back to contents, use Content () :

// returns the contents as the Hello 
mockMvc.perform (MockMvcRequestBuilders.get ( "/ index"))
  .andExpect (MockMvcResultMatchers.content () String ( "the Hello").); // Returns the content is XML, and as with xmlCotent mockMvc .perform (MockMvcRequestBuilders.get ( "/ index"))   .andExpect (MockMvcResultMatchers.content () XML (xmlContent).); // returns content is JSON, and as with jsonContent mockMvc.perform (MockMvcRequestBuilders.get ( "/ index "))   .andExpect (MockMvcResultMatchers.content () JSON (jsonContent)).;







 

Output response Results:

mockMvc.perform(MockMvcRequestBuilders.get("/index"))
  .andDo(MockMvcResultHandlers.print());

 

Testing Service

Existing follows Service:

@Repository("userService")
public class UserServiceImpl extends BaseService<User> implements UserService {

  @Override
  public User findByName(String userName) {
      Example example = new Example(User.class);
      example.createCriteria().andCondition("username=", userName);
      List<User> userList = this.selectByExample(example);
      if (userList.size() != 0)
          return userList.get(0);
      else
          return null;
  }
}

 

The Service to write a unit test, test findByName method is valid:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {

  @Autowired
  UserService userService;

  @Test
  public void test() {
      User user = this.userService.findByName("scott");
      Assert.assertEquals("用户名为scott", "scott", user.getUsername());
  }
}

 

After running, JUnit no description being given by the test, i.e. UserService the findByName feasible.

In addition, references in the Controller and Service compared Service after testing is complete, data can be automatically rolled back in the test cell, only you need to add on a test method @Transactional annotation, such as:

@Test
@Transactional
public void test() {
  User user = new User();
  user.setId(this.userService.getSequence("seq_user"));
  user.setUsername("JUnit");
  user.setPasswd("123456");
  user.setStatus("1");
  user.setCreateTime(new Date());
  this.userService.save(user);
}

 

Run, test passed, view database data has not been inserted, so good to avoid unnecessary data pollution.

Test Controller

Existing follows Controller:

@RestController
public class UserController {
  @Autowired
  UserService userService;

  @GetMapping("user/{userName}")
  public User getUserByName(@PathVariable(value = "userName") String userName) {
      return this.userService.findByName(userName);
  }

  @PostMapping("user/save")
  public void saveUser(@RequestBody User user) {
      this.userService.saveUser(user);
  }
}

 

Now write to the one for the Controller getUserByName (@PathVariable (value = "userName") String userName) test class methods:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {

  private MockMvc mockMvc;
   
  @Autowired
  private WebApplicationContext wac;
   
  @Before
  public void setupMockMvc(){
      mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
  }
   
  @Test
  public void test() throws Exception {
      mockMvc.perform(
          MockMvcRequestBuilders.get("/user/{userName}", "scott")
          .contentType(MediaType.APPLICATION_JSON_UTF8))
      .andExpect(MockMvcResultMatchers.status().isOk())
      .andExpect(MockMvcResultMatchers.jsonPath("$.username").value("scott"))
      .andDo(MockMvcResultHandlers.print());
  }
}

 

After running, JUnit by console output process is as follows:

MockHttpServletRequest:
    HTTP Method = GET
    Request URI = /user/scott
      Parameters = {}
        Headers = {Content-Type=[application/json;charset=UTF-8]}

Handler:
            Type = demo.springboot.test.controller.UserController
          Method = public demo.springboot.test.domain.User demo.springboot.test.controller.UserController.getUserByName(java.lang.String)

Async:
  Async started = false
    Async result = null

Resolved Exception:
            Type = null

ModelAndView:
      View name = null
            View = null
          Model = null

FlashMap:
      Attributes = null

MockHttpServletResponse:
          Status = 200
  Error message = null
        Headers = {Content-Type=[application/json;charset=UTF-8]}
    Content type = application/json;charset=UTF-8
            Body = {"id":23,"username":"scott","passwd":"ac3af72d9f95161a502fd326865c2f15","createTime":1514535399000,"status":"1"}
  Forwarded URL = null
  Redirected URL = null
        Cookies = []

 

To continue to prepare one for the the Controller saveUser (@RequestBody the User the User) test class methods:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {

  private MockMvc mockMvc;
   
  @Autowired
  private WebApplicationContext wac;
   
  @Autowired
  ObjectMapper mapper;
   
   
  @Before
  public void setupMockMvc(){
      mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
  }

  @Test
  @Transactional
  public void test() throws Exception {
      User user = new User();
      user.setUsername("Dopa");
      user.setPasswd("ac3af72d9f95161a502fd326865c2f15");
      user.setStatus("1");
       
      String userJson = mapper.writeValueAsString(user);
      mockMvc.perform(
          MockMvcRequestBuilders.post("/user/save")
          .contentType(MediaType.APPLICATION_JSON_UTF8)
          .content(userJson.getBytes()))
      .andExpect(MockMvcResultMatchers.status().isOk())
      .andDo(MockMvcResultHandlers.print());
  }
}

 

Operation process is as follows:

MockHttpServletRequest:
    HTTP Method = POST
    Request URI = /user/save
      Parameters = {}
        Headers = {Content-Type=[application/json;charset=UTF-8]}

Handler:
            Type = demo.springboot.test.controller.UserController
          Method = public void demo.springboot.test.controller.UserController.saveUser(demo.springboot.test.domain.User)

Async:
  Async started = false
    Async result = null

Resolved Exception:
            Type = null

ModelAndView:
      View name = null
            View = null
          Model = null

FlashMap:
      Attributes = null

MockHttpServletResponse:
          Status = 200
  Error message = null
        Headers = {}
    Content type = null
            Body =
  Forwarded URL = null
  Redirected URL = null
        Cookies = []

 

It is noteworthy that, when writing the test unit in a complete system, you may need to simulate a user logon information Session, MockMvc also provides a solution that can simulate a HttpSession initialization time:

Guess you like

Origin www.cnblogs.com/7788IT/p/11626826.html