You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot测试中MvcUriComponentsBuilder调用报No WebApplicationContext异常

解决MvcUriComponentsBuilder在@WebMvcTest中抛出No WebApplicationContext的问题

这个问题的核心在于你测试控制器的方式不对,以及MvcUriComponentsBuilder.fromMappingName的工作机制:

为什么会报错?

MvcUriComponentsBuilder.fromMappingName需要依赖WebApplicationContext来根据方法名(这里的"getById")查找对应的请求映射。但你现在是直接调用控制器的add方法,手动传入UriComponentsBuilder.newInstance()——这个实例不是由Spring容器管理的,也没有关联到测试的Web上下文,所以方法调用时找不到所需的上下文,就抛出了IllegalArgumentException。

而且@WebMvcTest是切片测试,虽然会初始化Spring MVC相关的上下文,但直接调用控制器方法时,并不会触发Spring的参数注入逻辑,你相当于在普通Java方法调用里传了个空的UriComponentsBuilder,自然无法支撑fromMappingName的功能。

正确的测试方式:用MockMvc模拟请求

最优解是使用Spring提供的MockMvc来模拟HTTP请求,这样Spring会自动处理参数注入(包括UriComponentsBuilder),并且MvcUriComponentsBuilder能正常获取到Web上下文。修改你的测试代码如下:

@ExtendWith(SpringExtension.class)
@WebMvcTest(PersonController.class)
class PersonControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @Test
    void add() throws Exception {
        PersonForCreate requestBody = new PersonForCreate("Charles", "Darwin");
        
        mockMvc.perform(post("/") // 对应你的@PostMapping路径
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(requestBody)))
                .andExpect(status().isCreated())
                .andExpect(header().exists("Location"));
    }

    // 如果你需要ObjectMapper,可以自动注入
    @Autowired
    private ObjectMapper objectMapper;
}

这样测试会模拟真实的HTTP请求,Spring会正确注入UriComponentsBuilder,MvcUriComponentsBuilder.fromMappingName也能正常工作,同时还能验证响应状态码和Location头是否符合预期。

替代方案:直接调用控制器方法的修复(不推荐)

如果你一定要直接调用控制器的add方法,需要手动给MvcUriComponentsBuilder设置WebApplicationContext,并且传入Spring管理的UriComponentsBuilder实例:

@ExtendWith(SpringExtension.class)
@WebMvcTest(PersonController.class)
class PersonControllerTest {
    @Autowired
    private PersonController personController;
    @Autowired
    private WebApplicationContext webApplicationContext;
    @Autowired
    private UriComponentsBuilder uriComponentsBuilder;

    @Test
    void add() {
        // 手动设置WebApplicationContext到MvcUriComponentsBuilder
        MvcUriComponentsBuilder.setWebApplicationContext(webApplicationContext);
        
        ResponseEntity<Person> response = this.personController.add(
                new PersonForCreate("Charles", "Darwin"), 
                uriComponentsBuilder
        );
        
        // 验证响应
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
        assertThat(response.getHeaders().getLocation()).isNotNull();
    }
}

不过这种方式跳过了Spring MVC的请求处理流程,测试的价值不如用MockMvc的方式高,只适合某些特殊场景。

额外优化:简化URI生成代码

另外,你可以简化控制器里的URI生成代码,用MvcUriComponentsBuilder.fromMethodCall来替代fromMappingName,这样不需要依赖方法名查找,代码更直观:

@PostMapping
public ResponseEntity<Person> add(@RequestBody final PersonForCreate personForCreate, UriComponentsBuilder uriComponentsBuilder) {
    Person newPerson = new Person(this.people.size() + 1, personForCreate.getFirstName(), personForCreate.getLastName());
    this.people.add(newPerson);
    
    URI uri = MvcUriComponentsBuilder.fromMethodCall(
            MvcUriComponentsBuilder.on(PersonController.class).getById(newPerson.getId())
    ).build().toUri();
    
    return ResponseEntity.created(uri).body(newPerson);
}

这种方式通过方法引用直接定位到getById方法,不需要依赖Web上下文查找方法名,在测试和生产环境都能更稳定工作。

内容的提问来源于stack exchange,提问作者Mekswoll

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 11:37:32