首页 > 解决方案 > 我的控制器代码无法从 MockMvc 获取请求中访问,我总是收到 404

问题描述

我正在尝试模拟我的控制器并为其编写测试用例。但是当我尝试调试 Test 类时,控件没有进入我的 Controller 类。我不确定我在这里做错了什么。

请帮助我解决这个问题,因为我被困在这个问题上几乎超过 3 个小时。

我的服务类


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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.bnpp.leavemanagement.dao.DepartmentRepository;
import com.bnpp.leavemanagement.model.DepartmentModel;

@Service
public class DepartmentService 
{
    @Autowired
    DepartmentRepository depRepo;
    
    public List<DepartmentModel> listDepartment = new ArrayList<>();
    
    public List<DepartmentModel> getAllDepartments()
    {
        List<DepartmentModel> allDepartment = depRepo.findAll();
        return allDepartment;
    }
    
    public DepartmentModel fetchDepartmentById( int id)
    {
        DepartmentModel depDetail = null;
        try
        {
            depDetail = depRepo.findById(Long.valueOf(id)).get();
        }
        catch(Exception ex)
        {
            depDetail = null;
        }
        return depDetail;
    }
    
    public DepartmentModel addDepartment(DepartmentModel depDetail)
    {
        DepartmentModel depExists = null;
        if (listDepartment.size() > 0) 
        {
            depExists = listDepartment.stream()
                    .filter(d -> d.getName().equalsIgnoreCase(depDetail.getName()))
                    .findFirst()
                    .orElse(null);
        }
        
        //Below condition is to restrict duplicate department creation
        if(depExists == null)
        {
            depRepo.save(depDetail);
            listDepartment.add(depDetail);
        }
        else
        {
            return null;
        }
        
        return depDetail;
    }
    
    public DepartmentModel updateDepartment(DepartmentModel depDetail)
    {
        DepartmentModel depUpdate = null;
        try
        {
            depUpdate = depRepo.findById(depDetail.getId()).get();
            depUpdate.setName(depDetail.getName());
            depRepo.save(depUpdate);
        } 
        catch(Exception ex)
        {
            depUpdate = null;
        }
        return depUpdate;
    }
    
    public DepartmentModel deleteDepartment(DepartmentModel depDetail)
    {
        try
        {
            depRepo.deleteById(depDetail.getId());
        }
        catch(Exception ex)
        {
            return null;
        }
        return depDetail;
    }
    
}

我的测试班


import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

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

import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
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.context.annotation.Description;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.web.context.WebApplicationContext;

import com.bnpp.leavemanagement.controller.DepartmentController;
import com.bnpp.leavemanagement.model.DepartmentModel;
import com.bnpp.leavemanagement.service.DepartmentService;

@ExtendWith(MockitoExtension.class)
@WebMvcTest(DepartmentController.class)
@ContextConfiguration(classes = com.bnpp.leavemanagementsystem.LeaveManagementSystemApplicationTests.class)
public class DepartmentControllerTest {

    @MockBean
    DepartmentService depService;
    
    @Autowired
    private MockMvc mockMvc;
    
    @InjectMocks
    DepartmentController departmentController;
    
    @Autowired
    private WebApplicationContext webApplicationContext;
    
    @Test
    @Description("Should return a list of DepartmentModel objects when called")
    void shouldReturnListOfDepartmentModel() throws Exception
    {
        DepartmentModel depModel = new DepartmentModel();
        depModel.setId(1L);
        depModel.setName("Mock");
        
        List<DepartmentModel> listDepmodel = new ArrayList<>();
        listDepmodel.add(depModel);
        
        Mockito.when(depService.getAllDepartments()).thenReturn(listDepmodel);
        
        mockMvc.perform(MockMvcRequestBuilders.get("/department"))
        .andDo(print())
        .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.size()", Matchers.is(1)))
                .andExpect(MockMvcResultMatchers.jsonPath("$[0].id").value(1))
                .andExpect(MockMvcResultMatchers.jsonPath("$[0].name").value("Mock"));
    }

}

我的控制器类

package com.bnpp.leavemanagement.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.bnpp.leavemanagement.dao.DepartmentRepository;
import com.bnpp.leavemanagement.model.DepartmentModel;
import com.bnpp.leavemanagement.service.DepartmentService;

@RestController
public class DepartmentController 
{
    @Autowired
    DepartmentService depService;
    
    
    @GetMapping("/department")
    public ResponseEntity<List<DepartmentModel>> getDepartments()
    {
        List<DepartmentModel> allDepartment = depService.getAllDepartments();
        if(allDepartment.size() == 0)
        {
            return new ResponseEntity( HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity( allDepartment, HttpStatus.OK);
    }
    
    @GetMapping("/department/{id}")
    public ResponseEntity<DepartmentModel> fetchDepartmentById(@PathVariable("id") int id)
    {
        DepartmentModel resDep = depService.fetchDepartmentById(id);
        if(resDep == null)
        {
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        }
        else
        {
            return new ResponseEntity( resDep, HttpStatus.OK);
        }
    }
    
    @PostMapping("/department/create")
    public ResponseEntity<DepartmentModel> createDepartment(@RequestBody DepartmentModel depNew)
    {
        DepartmentModel resDep = depService.addDepartment(depNew);
        if(resDep == null)
        {
            return new ResponseEntity(HttpStatus.EXPECTATION_FAILED);
        }
        else
        {
            return new ResponseEntity( resDep, HttpStatus.CREATED);
        }
    }
    
    @PutMapping("/department/update")
    public ResponseEntity<DepartmentModel> updateDepartment(@RequestBody DepartmentModel depNew)
    {
        DepartmentModel resDep = depService.updateDepartment(depNew);
        if(resDep == null)
        {
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        }
        else
        {
            return new ResponseEntity( resDep, HttpStatus.OK);
        }
    }
    
    @DeleteMapping("/department/delete")
    public ResponseEntity<DepartmentModel> deleteDepartment(@RequestBody DepartmentModel depDel)
    {
        DepartmentModel resDep = depService.deleteDepartment(depDel);
        if(resDep == null)
        {
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        }
        else
        {
            return new ResponseEntity(HttpStatus.OK);
        }
    }
    
}
 

标签: mockitojunit5spring-mvc-testspring-starter

解决方案


MockMvc使用的最小可行测试设置@WebMvcTest如下:

@WebMvcTest(DepartmentController.class)
class DepartmentControllerTest {

    @MockBean
    DepartmentService depService;
    
    @Autowired
    private MockMvc mockMvc;

    @Test
    @Description("Should return a list of DepartmentModel objects when called")
    void shouldReturnListOfDepartmentModel() throws Exception {
        DepartmentModel depModel = new DepartmentModel();
        depModel.setId(1L);
        depModel.setName("Mock");
        
        List<DepartmentModel> listDepmodel = new ArrayList<>();
        listDepmodel.add(depModel);
        
        Mockito.when(depService.getAllDepartments()).thenReturn(listDepmodel);
        
        mockMvc.perform(MockMvcRequestBuilders.get("/department"))
        .andDo(print())
        .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.size()", Matchers.is(1)))
                .andExpect(MockMvcResultMatchers.jsonPath("$[0].id").value(1))
                .andExpect(MockMvcResultMatchers.jsonPath("$[0].name").value("Mock"));
    }
}

解释:

  • @InjectMocks没有必要,因为@MockBeanDepartmentService要向 Spring TestContext 添加模拟版本,并且 Spring DI 的机制会将其注入到您的DepartmentController

  • @ExtendWith(SpringExtension.class)也是多余的,因为元注释@WebMvcTest已经激活了SpringExtension.class

  • 您不需要@ContextConfiguration在这里@WebMvcTest注意检测您的 Spring Boot 入口点类并启动切片上下文

如果测试仍然不起作用,请添加所有依赖项、构建代码的方式以及主 Spring Boot 类(用 注释@SpringBootApplication)。

进一步阅读可能会对此有所了解:


推荐阅读