如何为@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
相关产品推荐
相关产品推荐

