代码之家  ›  专栏  ›  技术社区  ›  elvis

如何使用@WebMvcTest for Unit Test POST方法?

  •  2
  • elvis  · 技术社区  · 6 年前

    这是REST控制器的代码:

    package com.dgs.restfultesting.controller;
    
    import java.net.URI;
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
    
    import com.dgs.restfultesting.business.ItemBusinessService;
    import com.dgs.restfultesting.model.Item;
    
    @RestController
    public class ItemController {
    
        @Autowired
        private ItemBusinessService businessService;
    
        @GetMapping("/all-items-from-database")
        public List<Item> retrieveAllItems() {
            return businessService.retrieveAllItems(); 
        }
    
        @PostMapping("/items")
        public Item addItem(@RequestBody Item item) {
            Item savedItem = businessService.addAnItem(item); 
    
            return savedItem;
        }
    }
    

    业务层:

    package com.dgs.restfultesting.business;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import com.dgs.restfultesting.data.ItemRepository;
    import com.dgs.restfultesting.model.Item;
    
    @Component
    public class ItemBusinessService {
    
        @Autowired
        private ItemRepository repository;
    
        public Item retrieveHardcodedItem() {
            return new Item(1, "Book", 10, 100); 
        }
    
        public List<Item> retrieveAllItems() {
    
            List<Item> items = repository.findAll(); 
    
            for (Item item : items) {
                item.setValue(item.getPrice() * item.getQuantity());  
            }
    
            return items;  
        }
    
        public Item addAnItem(Item item) {
            return repository.save(item); 
        }
    }
    

    项目控制器测试:

    package com.dgs.restfultesting.controller;
    
    import static org.hamcrest.CoreMatchers.containsString;
    import static org.junit.Assert.assertEquals;
    import static org.mockito.Mockito.when;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    
    import java.util.Arrays;
    
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    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.test.context.junit4.SpringRunner;
    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 com.dgs.restfultesting.business.ItemBusinessService;
    import com.dgs.restfultesting.model.Item;
    
    @RunWith(SpringRunner.class)             
    @WebMvcTest(ItemController.class)  
    public class ItemControllerTest {
    
        @Autowired
        private MockMvc mockMvc;    
    
        @MockBean
        private ItemBusinessService businessService;
    
        @Test
        public void retrieveAllItems_basic() throws Exception {
    
            when(businessService.retrieveAllItems()).thenReturn(
                    Arrays.asList(new Item(2, "iPhone", 1000, 10),
                            new Item(3, "Huawei", 500, 17)));
    
            RequestBuilder request = MockMvcRequestBuilders
                    .get("/all-items-from-database") 
                    .accept(MediaType.APPLICATION_JSON); 
    
            MvcResult result = mockMvc.perform(request)
                    .andExpect(status().isOk())
                    .andExpect(content().json("[{id:2, name:iPhone, price:1000}, {id:3, name:Huawei, price:500}]"))  // This will return an array back, so this data should be within an array
                    .andReturn();  
        }   
    
        @Test
        public void createItem() throws Exception {
            RequestBuilder request = MockMvcRequestBuilders
                    .post("/items")
                    .accept(MediaType.APPLICATION_JSON)
                    .content("{\"id\":1,\"name\":\"Book\",\"price\":10,\"quantity\":100}")
                    .contentType(MediaType.APPLICATION_JSON);
    
            MvcResult result = mockMvc.perform(request)
                    .andExpect(status().isCreated())
                    .andExpect(header().string("location", containsString("/item/")))
                    .andReturn();
        }
    }
    

    这是我在控制台收到的信息:

    MockHttpServletRequest:
          HTTP Method = POST
          Request URI = /items
           Parameters = {}
              Headers = {Content-Type=[application/json], Accept=[application/json]}
                 Body = <no character encoding set>
        Session Attrs = {}
    
    Handler:
                 Type = com.dgs.restfultesting.controller.ItemController
               Method = public com.dgs.restfultesting.model.Item com.dgs.restfultesting.controller.ItemController.addItem(com.dgs.restfultesting.model.Item)
    
    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 = []
    2018-10-07 17:53:51.457  INFO 55300 --- [       Thread-3] o.s.w.c.s.GenericWebApplicationContext   : Closing org.springframework.web.context.support.GenericWebApplicationContext@71075444: startup date [Sun Oct 07 17:53:48 EEST 2018]; root of context hierarchy
    

    更新-----------------------------

    这是控制器的代码:

    @PostMapping("/items")
    public ResponseEntity<Object> addItem(@RequestBody Item item) {
        Item savedItem = businessService.addAnItem(item); 
        HttpHeaders httpHeaders = new HttpHeaders();
        UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.newInstance();
    
        UriComponents uriComponents =
                uriComponentsBuilder.path("/item/{id}").buildAndExpand(savedItem.getId());
        httpHeaders.setLocation(uriComponents.toUri());
    
        return new ResponseEntity<>(savedItem, httpHeaders, HttpStatus.CREATED); 
    }
    

    这是测试代码:

    @Test
    public void createItem() throws Exception {
    
        RequestBuilder request = MockMvcRequestBuilders
                .post("/items")
                .accept(MediaType.APPLICATION_JSON)
                .content("{\"id\":1,\"name\":\"Book\",\"price\":10,\"quantity\":100}")
                .contentType(MediaType.APPLICATION_JSON);
    
        MvcResult result = mockMvc.perform(request)
                .andExpect(status().isCreated())
                .andExpect(header().string("location", containsString("/item/1")))
                .andReturn();
    }
    

    当我为createItem()方法运行JUnit测试时,我得到以下错误:org.springframework.web.util.NestedServletException:请求处理失败;嵌套异常是java.lang.NullPointerException

    2 回复  |  直到 6 年前
        1
  •  3
  •   halfer    6 年前

    从控制器返回201: 正如断言测试所期望的那样 201 通过使用 created 状态,但控制器返回200(正常)。

       @PostMapping("/items")
        public ResponseEntity<?> addItem(@RequestBody Item item) {
            Item savedItem = itemBusinessService.addAnItem(item);
    
            return new ResponseEntity<>(savedItem, HttpStatus.CREATED);
        }
    

    如果不想断言“location”,请更新测试。

     @Test
     public void createItem() throws Exception {
     RequestBuilder request = MockMvcRequestBuilders
            .post("/items")
            .accept(MediaType.APPLICATION_JSON)
            .content("{\"id\":1,\"name\":\"Book\",\"price\":10,\"quantity\":100}")
            .contentType(MediaType.APPLICATION_JSON);
    
    MvcResult result = mockMvc.perform(request)
            .andExpect(status().isOk()).andReturn();
    }
    

    UPDATE—允许位置头响应

    “位置” 若要从标题返回,请修改下面的控制器和测试用例以检查标题中的位置。

     @PostMapping("/items")
        public ResponseEntity<?> addItem(@RequestBody Item item) {
            Item savedItem = businessService.addAnItem(item);
            HttpHeaders httpHeaders = new HttpHeaders();
            UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.newInstance();
    
            UriComponents uriComponents =
                    uriComponentsBuilder.path("/item/").buildAndExpand("/item/");
            httpHeaders.setLocation(uriComponents.toUri());
            return new ResponseEntity<>(savedItem, httpHeaders, HttpStatus.CREATED);
        }
    

    步骤2:现在您的测试将断言 "location" 如你所料。

     @Test
        public void createItem() throws Exception {
            RequestBuilder request = MockMvcRequestBuilders
                    .post("/items")
                    .accept(MediaType.APPLICATION_JSON)
                    .content("{\"id\":1,\"name\":\"Book\",\"price\":10,\"quantity\":100}")
                    .contentType(MediaType.APPLICATION_JSON);
    
            MvcResult result = mockMvc.perform(request)
                    .andExpect(status().isCreated())
                    .andExpect(header().string("location", containsString("/item/")))
                    .andReturn();
        }
    
        2
  •  1
  •   Valerio Vaudi    6 年前

    Item item = new Item();
    Item newItem = new Item();
    when(businessService.addAnItem(item)).thenReturn(newItem);
    

    在你的控制器里我看不到位置头。可能下面这样的代码应该更好:

    @PostMapping("/items")
    public ResponseEntity<?> addItem(@RequestBody Item item) {
        Item savedItem = itemBusinessService.addAnItem(item);
    
        return ResponseEntity.created(UriComponentsBuilder.fromHttpUrl("http://yourserver/item"));
    }
    

    我希望这能对你有所帮助