SpringBoot測試Controller層


一、准備工作

  1、導入測試依賴

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

  2、Controller層:

@RestController("/")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping
    public String index(){
        return "Hello World!";
    }

    @GetMapping("/user")
    public ResponseEntity<List<User>> listUser(){
        List<User> list = new ArrayList<>();
        list.add(new User(1,"張三"));
        list.add(new User(2,"李四"));
        return new ResponseEntity(list,HttpStatus.OK);
    }

    @GetMapping("/user/{userId}")
    public ResponseEntity<User> getInfo(@PathVariable("userId") Integer userId){
        User user  = userService.findByUserId(userId);
        return new ResponseEntity(user,HttpStatus.OK);
    }

}

  3、UserService實現如下:

@Service
public class UserServiceImpl implements UserService {

    @Override
    public User findByUserId(Integer userId) {
        return  new User(userId,"用戶" + userId);
    }
}

二、測試

  1、創建第一個測試用例:

    在類上添加@RunWith和@SpringBootTest表示是一個可以啟動容器的測試類

@RunWith(SpringRunner.class)
@SpringBootTest //告訴SpringBoot去尋找主配置類(例如使用@SpringBootApplication注解標注的類),並使用它來啟動一個Spring application context;
public class UserController01Test {

    @Autowired
    private UserController userController;

    //測試@SpringBootTest是否會將@Component加載到Spring application context
    @Test
    public void testContexLoads(){
        Assert.assertThat(userController,notNullValue());
    }

}

  2、Spring Test支持的一個很好的特性是應用程序上下文在測試之間緩存,因此如果在測試用例中有多個方法,或者具有相同配置的多個測試用例,它們只會產生啟動應用程序一次的成本。使用@DirtiesContext注解可以清空緩存,讓程序重新加載。

  將上面代碼改造如下:在兩個方法上都添加@DirtiesContext注解,運行整個測試類,會發現容器加載了兩次。

            

  3、啟動服務器對Controller進行測試:

    這種方式是通過將TestRestTemplate注入進來,進行發送請求測試,缺點是需要啟動服務器。

@RunWith(SpringRunner.class)
//SpringBootTest.WebEnvironment.RANDOM_PORT設置隨機端口啟動服務器(有助於避免測試環境中的沖突)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 
public class HttpRequestTest {

    //使用@LocalServerPort將端口注入進來
    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void greetingShouldReturnDefaultMessage() throws Exception {
        Assert.assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/",String.class),
                Matchers.containsString("Hello World"));
    }
}

  4、使用@AutoConfigureMockMvc注解自動注入MockMvc:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc //不啟動服務器,使用mockMvc進行測試http請求。啟動了完整的Spring應用程序上下文,但沒有啟動服務器
public class UserController02Test {

    @Autowired
    private MockMvc mockMvc;

    /**
     * .perform() : 執行一個MockMvcRequestBuilders的請求;MockMvcRequestBuilders有.get()、.post()、.put()、.delete()等請求。
     * .andDo() : 添加一個MockMvcResultHandlers結果處理器,可以用於打印結果輸出(MockMvcResultHandlers.print())。
     * .andExpect : 添加MockMvcResultMatchers驗證規則,驗證執行結果是否正確。
     */
    @Test
    public void shouldReturnDefaultMessage() throws Exception {
        this.mockMvc.perform(get("/"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string(containsString("Hello World")));
    }

}

  5、使用@WebMvcTest只初始化Controller層

@RunWith(SpringRunner.class)
//使用@WebMvcTest只實例化Web層,而不是整個上下文。在具有多個Controller的應用程序中,
// 甚至可以要求僅使用一個實例化,例如@WebMvcTest(UserController.class)
@WebMvcTest(UserController.class)
public class UserController03Test {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void shouldReturnDefaultMessage() throws Exception {
        this.mockMvc.perform(get("/"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string(containsString("Hello World")));
    }

}

  上面的代碼會報錯,因為我們使用@WebMvcTest只初始化Controller層,但是在UserController 中注入了UserService,所以報錯。我們把代碼注釋如下,該測試就會成功了。    

    

  但是一般的Controller成都會引用到Service吧,怎么辦呢,我們可以使用mockito框架的@MockBean注解進行模擬,改造后的代碼如下:

@RunWith(SpringRunner.class)
//使用@WebMvcTest只實例化Web層,而不是整個上下文。在具有多個Controller的應用程序中,
// 甚至可以要求僅使用一個實例化,例如@WebMvcTest(UserController.class)
@WebMvcTest(UserController.class)
public class UserController03Test {

    @Autowired
    private MockMvc mockMvc;

    //模擬出一個userService
    @MockBean
    private UserService userService;

    @Test
    public void greetingShouldReturnMessageFromService() throws Exception {
        //模擬userService.findByUserId(1)的行為
        when(userService.findByUserId(1)).thenReturn(new User(1,"張三"));

        String result = this.mockMvc.perform(get("/user/1"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(jsonPath("$.name").value("張三"))
                .andReturn().getResponse().getContentAsString();

        System.out.println("result : " + result);
    }

}

 

 

資料:

  json-path : https://github.com/json-path/JsonPath

  mockito官網:https://site.mockito.org/

  spring官網用例:https://spring.io/guides/gs/testing-web/

  spring mockMvc文檔:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/MockMvc.html

   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM