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

Spring Boot Test测试Controller报model attribute不存在

问题说明

对返回带模型数据的视图接口做单元测试时,testInitUpdateAuthor() 方法执行过程中无法找到指定名称的author模型属性,其余接口的模型、属性相关测试均可正常运行,涉及代码如下:

@Slf4j
@Controller
public class AuthorController {

    private final AuthorService authorService;
    private final String CREATEORUPDATEFORM = "author/createOrUpdateAuthor";

    public AuthorController(AuthorService authorService) {
        this.authorService = authorService;
    }

    @GetMapping("/author/{id}/update")
    public String updateAuthor(@PathVariable("id") Long id, Model model) {
        model.addAttribute("author", authorService.findById(id));
        return CREATEORUPDATEFORM;
    }
}
@ExtendWith(MockitoExtension.class)
@SpringBootTest
class AuthorControllerTest {

    MockMvc mockMvc;

    @Mock
    AuthorService authorService;

    @InjectMocks
    AuthorController authorController;

    @BeforeEach
    void setUp() {
        mockMvc = MockMvcBuilders.standaloneSetup(authorController).build();
    }

    @Test
    void getIndex() throws Exception {
        mockMvc.perform(get("/author/index"))
                .andExpect(status().isOk())
                .andExpect(view().name("author/index"))
                .andExpect(model().attributeExists("authors"));
    }

    @Test
    void testInitUpdateAuthor() throws Exception {
        when(authorService.findById(anyLong())).thenReturn(Author.builder().id(1L).build());
        mockMvc.perform(get("/author/1/update"))
                .andExpect(status().isOk())
                .andExpect(view().name("author/createOrUpdateAuthor"))
                .andExpect(model().attributeExists("author"));
    }
}
排查建议

按以下优先级逐步定位即可:

  • 先确认请求是否真的命中了目标接口方法
    临时去掉model().attributeExists("author")的断言,在请求链式调用后加上.andDo(print())打印全量执行日志,重点看输出中Handler字段是否为AuthorController#updateAuthor(Long, Model):
    • 如果未命中目标方法,优先检查静态导入的get请求构建方法是否为Spring Test提供的(很多人会错导RestAssured等其他框架的同名方法导致请求异常);如果项目配置了全局上下文路径server.servlet.context-path,standaloneSetup模式默认不会读取该配置,要确认实际请求路径和接口映射路径完全匹配。
  • 检查模型属性实际的存储状态
    看打印日志中ModelAndView节点下的model属性列表:
    • 如果列表里完全没有author属性:检查AuthorController类上是否加了@SessionAttributes("author")注解,将该属性标记为了会话级别存储。standaloneSetup构建的MockMvc默认不会自动把会话属性暴露到request模型中,这种情况需要在perform请求时手动补充.sessionAttr("author", new Author()),或者调整测试配置。
    • 如果author属性存在但值为null:检查anyLong()参数匹配器的导入包是否正确,错导非Mockito包的参数匹配器会导致Mock触发条件不生效,authorService.findById()实际返回null;另外可以在setUp方法中打印authorController内部持有的authorService实例,确认@InjectMocks真的把Mock的Service实例注入进去了,没有出现注入失败的情况。
  • 解决测试配置冲突问题
    当前测试类同时使用了@SpringBootTest(加载Spring完整上下文)和Mockito standalone单元测试模式,两套实例管理逻辑很容易出现混淆:
    • 如果要做纯Controller层单元测试(不加载Spring上下文),直接删掉类上的@SpringBootTest注解,只保留@ExtendWith(MockitoExtension.class)即可,避免Spring容器生成的Controller实例干扰手动注入的Mock实例。
    • 如果要做Web层集成测试,就不要用standaloneSetup手动构建MockMvc,改用@AutoConfigureMockMvc注解直接注入Spring容器管理的MockMvc实例,同时用@MockBean注解替换容器内的真实AuthorService,不要再用@Mock+@InjectMocks的组合。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 11:42:16