Получение "По крайней мере одна метамодель JPA должна присутствовать" с помощью @WebMvcTest

Я новичок в 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: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!

В отличие от большинства людей, отправляющих эту ошибку, я не хочу использовать JPA для этого. Я пытаюсь использовать @WebMvcTest неправильно? Как я могу отследить волшебство Весны, которое приглашает JPA на эту вечеринку?

Ответ 1

Удалите все @EnableJpaRepositories или @EntityScan из вашего класса SpringBootApplication вместо этого сделайте следующее:

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 {

}

Ответ 2

У меня такая же проблема. @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
    }
}

Надеюсь, это поможет!

Ответ 3

Кроме того, вы можете определить собственный класс конфигурации внутри вашего тестового примера, включая только контроллер (плюс его зависимости), чтобы заставить Spring использовать этот контекст.
Обратите внимание, что вы все равно будете иметь доступ к 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 {}

Ответ 4

Если кто-то использует загрузку Spring и не хочет удалять @EntityScan и @EnableJpaRepositories вы можете удалить аннотацию @WebMvcTest из своего тестового класса и добавить вместо @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 над классом JpaMetamodelMappingContext вы JpaMetamodelMappingContext нужную метамодель.