使用@WebMvcTest 获取“必须存在至少一个 JPA 元模型”

Posted

技术标签:

【中文标题】使用@WebMvcTest 获取“必须存在至少一个 JPA 元模型”【英文标题】:Getting "At least one JPA metamodel must be present" with @WebMvcTest 【发布时间】:2017-05-06 03:11:51 【问题描述】:

我对 Spring 还很陌生,正在尝试为 @Controller 做一些基本的集成测试。

@RunWith(SpringRunner.class)
@WebMvcTest(DemoController.class)
public class DemoControllerIntegrationTests 
    @Autowired
    private MockMvc mvc;

    @MockBean
    private DemoService demoService;

    @Test
    public void index_shouldBeSuccessful() throws Exception 
        mvc.perform(get("/home").accept(MediaType.TEXT_html)).andExpect(status().isOk());
    

但我得到了

java.lang.IllegalStateException:无法加载 ApplicationContext 原因:org.springframework.beans.factory.BeanCreationException:创建名为“jpaMappingContext”的bean时出错:调用init方法失败;嵌套异常是 java.lang.IllegalArgumentException:必须存在至少一个 JPA 元模型! 原因:java.lang.IllegalArgumentException:必须存在至少一个 JPA 元模型!

与大多数发布此错误的人不同,我不想为此使用 JPA。我是否试图错误地使用@WebMvcTest?我怎样才能找到邀请 JPA 参加这个聚会的 Spring 魔法?

【问题讨论】:

嗨@Brad,你找到解决方案了吗?我的情况完全一样。 @Artegon 不是真的;我玩了各种各样的东西并最终克服了它,但对什么是错误的或修复它没有清楚的了解。贾斯汀的回答看起来很有希望 嗨@Artegon,请问您找到解决方案了吗,我也遇到了同样的情况 【参考方案1】:

我遇到了同样的问题。 @WebMvcTest 查找使用 @SpringBootApplication 注释的类(如果找不到,则在应用程序结构的同一目录中或更高的目录中)。您可以阅读其工作原理@@https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-mvc-tests。

如果您使用 @SpringBootApplication 注释的类也有 @EntityScan /@EnableJpaRepositories 会发生此错误。因为你有这些带有 @SpringBootApplication 的注释并且你正在模拟服务(所以实际上没有使用任何 JPA)。我找到了一种解决方法,它可能不是最漂亮的,但对我有用。

将此类放在您的测试目录(根目录)中。 @WebMvcTest 将在您的实际 Application 类之前找到此类。在这个类中你不必添加@EnableJpaRepositories/@EntityScan。

@SpringBootApplication(scanBasePackageClasses = 
    xxx.service.PackageMarker.class,
    xxx.web.PackageMarker.class
)
public class Application 

你的测试看起来会一样。

@RunWith(SpringRunner.class)
@WebMvcTest
@WithMockUser
public class ControllerIT 

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private Service service;

    @Test
    public void testName() throws Exception 
        // when(service.xxx(any(xxx.class))).thenReturn(xxx); 
        // mockMvc.perform(post("/api/xxx")...
        // some assertions
    

希望这会有所帮助!

【讨论】:

这是一个很好的解决 MVC 测试和 Spring Boot 之间恼人冲突的方法。似乎有必要将空应用程序类命名为与实际应用程序类相同的名称,并将其放在同一个包中(即,您的示例仅在应用程序实际命名为 Application 时才有效)。您可能想在回答中提及这一点。【参考方案2】:

SpringBootApplication 类中删除任何@EnableJpaRepositories@EntityScan,而不是这样做:

package com.tdk;

@SpringBootApplication
@Import(ApplicationConfig.class )
public class TdkApplication 

    public static void main(String[] args) 
        SpringApplication.run(TdkApplication.class, args);
    

并将其放在单独的配置类中:

package com.tdk.config;

@Configuration
@EnableJpaRepositories(basePackages = "com.tdk.repositories")
@EntityScan(basePackages = "com.tdk.domain")
@EnableTransactionManagement
public class ApplicationConfig 


这里是测试:

@RunWith(SpringRunner.class)
@WebAppConfiguration
@WebMvcTest
public class MockMvcTests 


【讨论】:

这很奇怪,但似乎不起作用。我尝试将@EntityScan@EnableJpaRepositories 注释分离到一个单独的配置类中,它位于应用程序类旁边。应用程序本身正确启动。然而,我的测试表明,应用程序上下文无法启动(提问者提到的错误)。在我的情况下,我确实想在实际应用程序中使用 Jpa,但我不想在我的 Controller-Test 中使用它。【参考方案3】:

或者,您可以在测试用例中定义一个自定义配置类,仅包括控制器(加上它的依赖项),以强制 Spring 使用 this 上下文。 请注意,您仍然可以访问 MockMvc 和您的测试用例中的其他优点,如果它带有 WebMvcTest 注释。

@RunWith(SpringRunner.class)
@WebMvcTest(DemoController.class)
public class DemoControllerIntegrationTests 
    @Autowired
    private MockMvc mvc;

    @MockBean
    private DemoService demoService;

    @Test
    public void index_shouldBeSuccessful() throws Exception 
        mvc.perform(get("/home").accept(MediaType.TEXT_HTML)).andExpect(status().isOk());
    

    @Configuration
    @ComponentScan(basePackageClasses =  DemoController.class )
    public static class TestConf 

【讨论】:

这似乎是一个很好的方法。但是,当我尝试它时,我得到另一个错误,因为对同一包中的不同 RestController 的依赖不满意。据我所知,唯一的关系是它们在同一个包中。如果@ComponentScan 只查看正在测试的类,那么不相关的类怎么会出错? @Tim ComponentScan 的 basePackageClasses 属性暗示“此包和所有子包中的所有组件类”。如果您的控制器共享相同的包,您可以使用包含/排除过滤器来细化范围。 或者,您也可以不使用@ComponentScan,而只使用@Configuration 注解,然后在TestConf 内添加@Bean public DemoController demoController() return new DemoController(); 。然后它只会配置那个控制器。 @Tim 使用包含/排除方法,您将获得 real bean。在测试配置中,大多数情况下我们希望模拟/覆盖依赖项,同时仍然针对被测的 real 类。【参考方案4】:

如果有人使用 Spring boot 并且不想删除 @EntityScan@EnableJpaRepositories,您可以从测试类中删除 @WebMvcTest 注释并添加以下内容:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class DemoControllerIntegrationTests 
    @Autowired
    private MockMvc mvc;

    //...

您将能够自动连接 MockMvc 并使用它。

【讨论】:

【参考方案5】:

@MockBean(JpaMetamodelMappingContext.class)添加到DemoControllerIntegrationTests类的上面:

@RunWith(SpringRunner.class)
@WebMvcTest(DemoController.class)
@MockBean(JpaMetamodelMappingContext.class)
public class DemoControllerIntegrationTests 
    ...

因为您没有在测试中使用数据库,所以 Spring 会抛出此异常。通过模拟 JpaMetamodelMappingContext 类,您将绕过所需的元模型。

【讨论】:

以上是关于使用@WebMvcTest 获取“必须存在至少一个 JPA 元模型”的主要内容,如果未能解决你的问题,请参考以下文章

spring jpa - 必须存在至少一个 JPA 元模型*

SpringBoot 2.0.2 必须存在至少一个 JPA 元模型

“IllegalArgumentException:必须存在至少一个 JPA 元模型” - 尝试将应用程序同时连接到 mongo 和 sql 时

使用 @WebMvcTest 测试中的 ApplicationContext 异常

SpringBoot @WebMvcTest,自动装配 RestTemplateBuilder

如何使用@WebMvcTest 春季测试在模拟服务中注入模拟的restTemplate