Spring Boot - 控制器测试失败并出现 404 代码

问题描述 投票:0回答:12

我想为控制器编写一个测试。这是测试片段:

@RunWith(SpringRunner.class)
@WebMvcTest(WeatherStationController.class)
@ContextConfiguration(classes = MockConfig.class)
public class WeatherStationControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private IStationRepository stationRepository;

    @Test
    public void shouldReturnCorrectStation() throws Exception {

        mockMvc.perform(get("/stations")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk());
    }
}

控制器代码片段:

@RestController
@RequestMapping(value = "stations")
public class WeatherStationController {

    @Autowired
    private WeatherStationService weatherService;

    @RequestMapping(method = RequestMethod.GET)
    public List<WeatherStation> getAllWeatherStations() {
        return weatherService.getAllStations();
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public WeatherStation getWeatherStation(@PathVariable String id) {
        return weatherService.getStation(id);
    }

MockConfig 类:

@Configuration
@ComponentScan(basePackages = "edu.lelyak.repository")
public class MockConfig {

    //**************************** MOCK BEANS ******************************

    @Bean
    @Primary
    public WeatherStationService weatherServiceMock() {
        WeatherStationService mock = Mockito.mock(WeatherStationService.class);
        return mock;
    }

这是错误堆栈跟踪:

java.lang.AssertionError: Status 
Expected :200
Actual   :404

我可以明白这里出了什么问题。
如何修复控制器测试?

spring unit-testing spring-boot controller
12个回答
29
投票

我也有同样的问题。尽管用

@WebMvcTest(MyController.class)
指定了控制器,但控制器并未被拾取。这意味着它的所有映射都被忽略,导致 404。添加
@Import(MyController.class)
解决了问题,但我没想到当我已经指定要测试哪个控制器时需要导入。


24
投票

HTTP代码404,意味着(在服务器上)找不到您的请求的资源,我认为您的控制器在Spring Boot中不可见(让我说没有扫描)。

一个简单的解决方案是扫描

MockConfig
类中的父包,这样Spring就可以拾取所有bean,

@ComponentScan(basePackages = "edu.lelyak") // assuming that's the parent package in your project

如果你不喜欢这种方式,你可以在

basePackages

中添加控制器的包名
@ComponentScan(basePackages = {"edu.lelyak.controller","edu.lelyak.repository") 

顺便说一句,你不必在

WeatherStationService
类中手动设置
MockConfig
,Spring boot 可以为你注入一个模拟并在每个测试方法后自动重置它,你应该在你的测试类中声明它:

@MockBean
private IStationRepository stationRepository;

另一方面,您应该在测试方法中调用

weatherService.getAllStations()
之前模拟
get("/stations")
(因为您没有运行集成测试),所以您可以这样做:

List<WeatherStation> myList = ...;
//Add element(s) to your list
 Mockito.when(stationService.getAllStations()).thenReturn(myList);

您可以在以下位置找到更多信息:


10
投票

经过一些调试,目标控制器似乎根本没有注册为方法处理程序。 Spring 扫描 bean 是否存在

RestController
注释。

但是问题是,只有当bean通过CGLIB代理时才能找到注释,但是对于我们使用

WebMvcTest
的情况,它是由JDK代理的。

结果我去寻找负责做出选择的配置,终于找到了

AopAutoConfiguration
。因此,当使用 SpringBootTest 时,当您在控制器中需要
WebMvcTest+PreAuthorize
时,会自动加载此测试,然后只需使用:

@Import(AopAutoConfiguration.class)

9
投票

我不确定为什么你的测试不起作用。但我得到了另一个适合我的解决方案。

@SpringBootTest
public class ControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.standaloneSetup(new TestController()).build();
    }

    @Test
    public void shouldReturnCorrectStation() throws Exception {
        mockMvc.perform(get("/stations")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk());
    }
}

4
投票

我通过

@ContextConfiguration(classes = MyConfig.class)

导入外部配置类

当我将

MyConfig
注释
@Configuration
更改为
@TestConfiguration
时,它开始正常工作。


3
投票

我找不到好的答案,但我可以找到原因之一。

我在测试中使用了 RestController 上的

@PreAuthorize

您可以在使用 SpringBootTest 的集成测试中使用
thistip
来模拟 Oauth。对于
SpringBootTest
,这也非常有效,但是使用
SpringBootTest
您会加载许多其他资源(例如 JPA),而这些资源对于进行简单的控制器测试来说是不必要的。

但是对于

@WebMvcTest
,这不会按预期工作。使用 WithMockOAuth2Scope 注释足以阻止身份验证问题导致的 401 错误,但之后 WebMvcTest 找不到其余端点,返回 404 错误代码。

移除控制器上的

@PreAuthorize
后,
WebMvcTest
测试通过。


3
投票

基于接受的答案,在我的例子中,我根据另一个测试复制并修改了文件,但忘记了更改类顶部控制器的名称,这就是它不存在的原因找到资源,如错误所示。

@RunWith(SpringRunner.class)
@WebMvcTest(AnswerCommandController.class)
public class AnswerCommandControllerTest {

1
投票

这是对我有用的控制器测试的不同方法。

假设:类

WeatherStationService
是一个
@SpringBootApplication

那么,下面的测试类应该适合您:

@RunWith(SpringRunner.class)
@SpringApplicationConfiguration(WeatherStationService.class)
@WebIntegrationTest
public class WeatherStationControllerTest {

    @Autowired
    private WebApplicationContext context;

    MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc =  MockMvcBuilders.webAppContextSetup(this.context).build();
    }

    @Test
    public void shouldReturnCorrectStation() throws Exception {
        mockMvc.perform(get("/stations")
                .accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk();
    }
}

通过此测试设置,您应该不再需要

MockConfig
类。


1
投票

就我而言,这是关于缺少起始斜杠

/

我已将

/
附加到
RequestMapping value
MockHttpServletRequestBuilder post urlTemplate
参数作为第一个字符。


1
投票

以防有人想知道。

如果我们不使用

@ContextConfiguration
@WebMvcTest
注释将加载REST控制器类。否则,当我们使用 use
@ContextConfiguration
时,似乎
ContextConfiguration
会清除上下文 REST 控制器配置。我们需要将 REST 控制器添加到
ContextConfiguration
,例如:
@ContextConfiguration(classes = {MockConfig.class, WeatherStationController.class})


0
投票

就我而言,我还必须将控制器添加到@ContextConfiguration。

@ContextConfiguration(classes = {MockConfig.class, WeatherStationController.class})

0
投票

看着这个答案,我认为问题可能在于使用

ContextConfiguration
创建正确的上下文。
同样在 WebMvcTest
documentations
中,它说:

通常 @WebMvcTest 与 @MockBean 或 @Import 结合使用来创建 @Controller beans 所需的任何协作者。

所以我用

ContextConfiguration
替换了
Import
并且成功了。

© www.soinside.com 2019 - 2024. All rights reserved.