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

如何为@ModelAttribute注解方法编写Mockito测试?模型属性未生效求助

问题分析与解决方案

你的测试没有达到预期效果,核心原因是你mock的ProjectsDao并没有被注入到控制器实例中。你使用了@SpringBootTest加载完整的Spring应用上下文,但@Mock注解创建的mock对象并没有被Spring容器管理,控制器仍然使用的是真实的ProjectsDao实例,所以你指定的异常并没有被触发。

下面给你两种可行的修复方案:

方案一:使用@MockBean替换@Mock(Spring上下文环境下)

@MockBean是Spring Boot提供的注解,它会自动在Spring容器中创建一个mock的ProjectsDao实例,并替换掉原有的真实bean,这样控制器就会使用这个mock对象。修改后的测试代码如下:

@SpringBootTest
@AutoConfigureMockMvc // 自动配置MockMvc,无需手动构建
class HomeControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean // 用MockBean代替Mock
    private ProjectsDao projectsDao;

    @Test
    public void homePageCatchesException_ifFeaturedProjectsAreNotFound() throws Exception {
        // 准备测试数据
        String errorMessage = "No featured projects available";
        doThrow(new FeaturedProjectsNotFoundException(errorMessage))
                .when(projectsDao).getFeaturedProjects();

        // 执行请求并验证结果
        this.mockMvc.perform(MockMvcRequestBuilders.get("/"))
                .andExpect(status().isOk())
                .andExpect(model().attributeExists("featuredProjects"))
                .andExpect(model().attribute("ProjectsNotFound", errorMessage));
    }
}

为什么这样能生效?

  • @MockBean会让Spring容器将mock的ProjectsDao注入到HomeController中,替换掉原本自动装配的真实DAO。
  • @AutoConfigureMockMvc会帮你自动配置MockMvc,省去手动构建的步骤,代码更简洁。

方案二:使用Mockito独立测试(无需加载Spring上下文)

如果你的测试不需要完整的Spring上下文,可以用Mockito的@InjectMocks和StandaloneMockMvcBuilder来构建轻量测试,这种方式运行速度更快:

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.mockito.Mockito.doThrow;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@ExtendWith(MockitoExtension.class) // 启用Mockito扩展
class HomeControllerTest {
    private MockMvc mockMvc;

    @Mock // 创建mock的ProjectsDao
    private ProjectsDao projectsDao;

    @InjectMocks // 将mock的DAO注入到控制器中
    private HomeController homeController;

    @BeforeEach
    public void setUp() {
        // 用独立模式构建MockMvc,只针对当前控制器
        this.mockMvc = MockMvcBuilders.standaloneSetup(homeController)
                .build();
    }

    @Test
    public void homePageCatchesException_ifFeaturedProjectsAreNotFound() throws Exception {
        String errorMessage = "No featured projects available";
        doThrow(new FeaturedProjectsNotFoundException(errorMessage))
                .when(projectsDao).getFeaturedProjects();

        this.mockMvc.perform(get("/"))
                .andExpect(status().isOk())
                .andExpect(model().attributeExists("featuredProjects"))
                .andExpect(model().attribute("ProjectsNotFound", errorMessage));
    }
}

额外小建议

你的控制器中的日志代码存在冗余,每次都new HomeController()来获取类信息是没必要的,可以直接用this.getClass()或者HomeController.class:

logger.info("{} {}", getClass().getPackage(), getClass());

这样不仅代码更简洁,还能避免不必要的对象创建。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 17:42:47