使用@WebMvcTest--使用MockMvc框架来模拟HTTP请求进行测试--实现对单个控制器的http模拟测试

1.优点

无需启动内置服务器就可以对Controller中某一个HTTP接口进行测试,减少电脑内存占用和运行springboot时间消耗

2.控制器类简单的方法

package com.xurong.chapter4_test.controller;

import com.xurong.chapter4_test.Entity.Book;
import com.xurong.chapter4_test.repository.ReadingListRepository;
import com.xurong.chapter4_test.service.BookService;
import com.xurong.chapter4_test.service.impl.BookServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;

/**
 * @author xu
 * @Description
 * @date 2023/7/19 - 9:50
 * @Modified By:
 */
@RequiredArgsConstructor
@RestController
@RequestMapping("/book")
public class BookController {

    private final BookService bookService;

    @GetMapping("/all")
    public List<Book> getAll() {
        return bookService.getAll();
    }
    @GetMapping("/{id}")
    public Book getById(@PathVariable("id") Long id) {
        return bookService.getById(id);
    }

    @PostMapping("/insert")
    public void insert(@Valid @RequestBody Book book) {
        bookService.insert(book);
    }


}

3.使用MockMvc框架来模拟HTTP请求进行测试

package com.xurong.chapter4_test.controller;

import com.xurong.chapter4_test.Entity.Book;
import com.xurong.chapter4_test.service.BookService;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import static org.hamcrest.Matchers.containsString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;


import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

/**
 * @author xu
 * @Description
 * @date 2023/7/19 - 16:38
 * @Modified By:
 */
@WebMvcTest(BookController.class)
class BookControllerTest {

    @MockBean
    private BookService bookService;

    @Autowired
    private MockMvc mockMvc;

    @Test
    void checkMockMvc() {
        // 检验MockMvc对象是否注入成功
        assertNotNull(mockMvc);
    }

    // 测试:@GetMapping("/all")
    @Test
    void getAll() throws Exception {
        List<Book> books = new ArrayList<>();
        // 模拟正确访问应该返回的List数据值
        Book book1 = new Book(1L, "张三", "300", "三国演义", "罗贯中", "诸葛亮,周瑜,曹操");
        Book book2 = new Book(2L, "xu", "300", "三国演义", "罗贯中", "诸葛亮,周瑜,曹操");
        books.add(book1);
        books.add(book2);

        when(bookService.getAll())
                .thenReturn(books);

        // 发送请求,对结果与上面模拟的结果值进行对比验证
        this.mockMvc.perform(get("/book/all"))
                .andDo(print())
                // 验证响应状态码为200
                .andExpect(status().isOk())
                // 检验List结果集合的大小为2
                .andExpect(jsonPath("$.size()").value(2))
                // 检验list结合中的第2个对象的reader属性值是否为“xu”
                .andExpect(jsonPath("$[1].reader").value("xu"));

    }

    // 测试:public Book getById(@PathVariable("id") Long id)
    @Test
    void getById() throws Exception {
        // 模拟正确访问应该返回的book数据值
        Book book2 = new Book(2L, "xu", "300", "三国演义", "罗贯中", "诸葛亮,周瑜,曹操");
        when(bookService.getById(2L)).thenReturn(book2);

        // 发送http请求,在请求路径上添加id值
        this.mockMvc.perform(
                MockMvcRequestBuilders.get("/book/{id}",2L))
                .andDo(print())
                .andExpect(jsonPath("$.reader").value("xu"));
    }

    // 测试:public void insert(@Valid @RequestBody Book book)
    // 对于@Valid也可以起到验证作用
    @Test
    void insert() throws Exception {
        this.mockMvc.perform(MockMvcRequestBuilders.post("/book/insert")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"reader\":\"小武a\",\"isbn\":\"400\",\"title\":\"水浒传\",\"author\":\"施耐庵\",\"description\":\"武松:打虎英雄\"}"))
                //使用andDo(print())方法打印出请求和响应的详细信息
                .andDo(print());

        // 使用verify方法验证BookService的insert方法是否成功被调用,并且传入的参数是一个Book对象。
        // 这可以通过any(Book.class)来实现,表示任意类型的Book对象都可以作为参数传入
        verify(bookService).insert(any(Book.class));
    }
}

4.需要注意的地方

注意1:如果BookControllerTest注入了BookService,那么在编写测试方法时需要使用@MockBean注入service的对象,注意不是使用原生的@Resource,@Autowired或者构造器进行注入

 @MockBean
 private BookService bookService;

注意2:需要将模拟容器注入进来,当然注入的方法有多种,网上自行搜索

@Autowired
private MockMvc mockMvc;

注意3:这一段时发送http请求,MockMvcRequestBuilders有多个方法,也可以添加请求头和请求体等信息

this.mockMvc.perform(
MockMvcRequestBuilders.get("/book/{id}",2L))

注意4:
后面的几个方法是对http请求结果进行验证,andDo方法将测试结果打印,andExpect对http请求结果的期望值;
注意一定要调用(when(bookService.getById(2L)).thenReturn(book2);),否则("$.reader")不会起作用

    @Test
    void getById() throws Exception {
        // 创建了一个Book对象book2,并使用when方法模拟了调用bookService.getById(2L)时应该返回book2对象的情况
        Book book2 = new Book(2L, "xu", "300", "三国演义", "罗贯中", "诸葛亮,周瑜,曹操");
        when(bookService.getById(2L)).thenReturn(book2);

        // 发送http请求,在请求路径上添加id值
        this.mockMvc.perform(
                MockMvcRequestBuilders.get("/book/{id}",2L))
                .andDo(print())
                .andExpect(jsonPath("$.reader").value("xu"));
    }

参考视频:
视频up-github地址
MockMvcRequestBuilders文档地址

posted @ 2023-07-19 19:45  远道而重任  阅读(291)  评论(0编辑  收藏  举报