Как я могу использовать @WebMvcTest для метода POST Unit Test? - PullRequest
0 голосов
/ 07 октября 2018

Я запускаю юнит-тесты с Spring Boot и Mockito и тестирую RESTful-сервисы.Когда я пытаюсь проверить метод GET, он работает успешно, но когда я пытаюсь проверить метод POST, он не работает.Что я должен сделать, чтобы решить эту проблему?Заранее спасибо!

Это код для контроллера 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); 
    }
}

ItemControllerTest:

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();
    }
}

Нет проблем с тестированием метода retrieveAllItems_basic (), но когда я пытаюсь запустить тест JUnit для метода createItem (), он не работает, и я получаю следующее: java.lang.AssertionError: Ожидаемое состояние: <201>но было: <200>

И вот что я получаю в консоли:

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

Обновление -----------------------------

Я пытаюсь установить местоположение следующим образом: item / id.

Это код для контроллера:

@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();
}

Когда я запускаю тест JUnit для метода createItem (), я получаю эту ошибку: org.springframework.web.util.NestedServletException: Обработка запроса не удалась;Вложенным исключением является java.lang.NullPointerException

Ответы [ 2 ]

0 голосов
/ 07 октября 2018

Прежде всего, я не вижу в вашем тесте createItem макет программной части, скажем,

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

, а в вашем контроллере я не вижу заголовок Location.Вероятно, код, подобный приведенному ниже, должен быть лучше:

@PostMapping("/items")
public ResponseEntity<?> addItem(@RequestBody Item item) {
    Item savedItem = itemBusinessService.addAnItem(item);

    return ResponseEntity.created(UriComponentsBuilder.fromHttpUrl("http://yourserver/item"));
}

Я надеюсь, что это может помочь вам

0 голосов
/ 07 октября 2018

Возврат 201 из вашего контроллера: Поскольку ваш тест подтверждения ожидает 201 при использовании статуса created, но ваш контроллер возвращает 200 (ОК).

   @PostMapping("/items")
    public ResponseEntity<?> addItem(@RequestBody Item item) {
        Item savedItem = itemBusinessService.addAnItem(item);

        return new ResponseEntity<>(savedItem, HttpStatus.CREATED);
    }

Или измените свой тест, чтобы проверить статус OK (200). Обновите свой тест, если вы не хотите утверждать «местоположение».

 @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();
}

ОБНОВЛЕНИЕ - Разрешить заголовок местоположенияв ответ

Если вы хотите, чтобы "location" возвратился из заголовка, то измените ваш контроллер и контрольный пример ниже, чтобы также проверить местоположение в заголовке.

STEP1: В методе add item вашего контроллера добавьте местоположение uri и вернитесь.

 @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();
    }
...