如何解决测试addViewControllers时ApplicationContext加载失败的异常
问题:JUnit5测试视图时出现"Given type must be an interface!"错误
作为新手,我正在编写一个极简的JUnit5测试用例来测试视图,但无论如何配置文件,都会反复出现相同的错误。我尝试了@SpringBootTest、@AutoConfigureMockMvc、@ExtendWith等注解的所有组合,也试过自动装配MockMvc和在setup方法中构建它,但都无效。
测试文件
package com.example; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import org.springframework.web.context.WebApplicationContext; import org.springframework.beans.factory.annotation.Autowired; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.context.support.StaticApplicationContext; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; // @ExtendWith(SpringExtension.class) @SpringBootTest @AutoConfigureMockMvc public class MyViewTest { @Autowired private MockMvc mockMvc; // private WebApplicationContext webApplicationContext; private ViewControllerRegistry registry; @BeforeEach public void setup() { this.registry = new ViewControllerRegistry(new StaticApplicationContext()); // this.mockMvc = MockMvcBuilders.standaloneSetup(webApplicationContext).build(); } @Test public void testAddViewControllers() throws Exception { MyView view = new MyView(); // 修正原代码笔误 view.addViewControllers(registry); mockMvc.perform(get("/")) .andExpect(status().isOk()) .andExpect(view().name("forward:/public/index")); } }
POM依赖片段
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>3.12.4</version> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>3.12.4</version> <scope>test</scope> </dependency>
视图类代码
package com.example; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class MyView implements WebMvcConfigurer { @Override public void addViewControllers( ViewControllerRegistry registry ) { registry.addViewController( "/" ).setViewName( "forward:/public/index" ); registry.setOrder( Ordered.HIGHEST_PRECEDENCE ); } }
异常堆栈跟踪(最终原因)
Caused by: java.lang.IllegalArgumentException: Given type must be an interface! at org.springframework.util.Assert.isTrue(Assert.java:121) ~[spring-core-5.3.6.jar:5.3.6] at org.springframework.data.repository.core.support.AbstractRepositoryMetadata.<init>(AbstractRepositoryMetadata.java:60) ~[spring-data-commons-2.4.8.jar:2.4.8] at org.springframework.data.repository.core.support.AnnotationRepositoryMetadata.<init>(AnnotationRepositoryMetadata.java:45) ~[spring-data-commons-2.4.8.jar:2.4.8] at org.springframework.data.repository.core.support.AbstractRepositoryMetadata.getMetadata(AbstractRepositoryMetadata.java:78) ~[spring-data-commons-2.4.8.jar:2.4.8] at org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport.getRepositoryConfigurations(RepositoryConfigurationExtensionSupport.java:103) ~[spring-data-commons-2.4.8.jar:2.4.8] at org.springframework.data.repository.config.RepositoryConfigurationDelegate.registerRepositoriesIn(RepositoryConfigurationDelegate.java:149) ~[spring-data-commons-2.4.8.jar:2.4.8] at org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport.registerBeanDefinitions(AbstractRepositoryConfigurationSourceSupport.java:62) ~[spring-boot-autoconfigure-2.4.5.jar:2.4.5] at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:396) ~[spring-context-5.3.6.jar:5.3.6] at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:684) ~[na:na] at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:395) ~[spring-context-5.3.6.jar:5.3.6] at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:157) ~[spring-context-5.3.6.jar:5.3.6] at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:129) ~[spring-context-5.3.6.jar:5.3.6] at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:343) ~[spring-context-5.3.6.jar:5.3.6] at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247) ~[spring-context-5.3.6.jar:5.3.6] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311) ~[spring-context-5.3.6.jar:5.3.6] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112) ~[spring-context-5.3.6.jar:5.3.6] at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) ~[spring-context-5.3.6.jar:5.3.6] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564) ~[spring-context-5.3.6.jar:5.3.6] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:782) ~[spring-boot-2.4.5.jar:2.4.5] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:774) ~[spring-boot-2.4.5.jar:2.4.5] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-2.4.5.jar:2.4.5] at org.springframework.boot.SpringApplication.run(SpringApplication.java:339) ~[spring-boot-2.4.5.jar:2.4.5] at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:123) ~[spring-boot-test-2.4.5.jar:2.4.5] at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99) ~[spring-test-5.3.6.jar:5.3.6] at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) ~[spring-test-5.3.6.jar:5.3.6] ... 67 common frames omitted
解决方案
1. 错误原因分析
堆栈信息显示错误来自Spring Data组件,核心原因是Spring Data尝试将一个非接口类识别为Repository。你使用@SpringBootTest会加载完整的Spring应用上下文,触发Spring Data的自动配置逻辑,它会扫描项目中的Repository类,但如果扫描到不符合要求的非接口类(比如扫描路径覆盖了非Repository类),就会抛出该异常。
而你实际只需要测试Web视图配置,完全不需要加载整个应用上下文。
2. 修改测试类配置
使用@WebMvcTest替代@SpringBootTest,这个注解只会加载Web层相关的Bean(包括WebMvcConfigurer实现类如MyView),不会加载Spring Data、数据库等无关组件,避免触发不必要的自动配置逻辑。
同时,你手动创建MyView和ViewControllerRegistry的代码是多余的,Spring会自动加载并应用MyView中的视图配置。
修改后的测试代码如下:
package com.example; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(MyView.class) // 指定加载目标WebMvc配置类 public class MyViewTest { @Autowired private MockMvc mockMvc; @Test public void testRootPathRedirect() throws Exception { mockMvc.perform(get("/")) .andExpect(status().isOk()) .andExpect(view().name("forward:/public/index")); } }
3. 额外注意事项
- 如果项目中确实有Spring Data依赖,确保所有Repository都是接口,并且正确标记
@Repository或继承Spring Data的标准Repository接口(如JpaRepository)。 @WebMvcTest默认会自动配置MockMvc,无需额外添加@AutoConfigureMockMvc注解。
内容的提问来源于stack exchange,提问作者wissotajava
相关产品推荐
相关产品推荐

