Brad Mace
Brad Mace

Reputation: 27886

Getting "At least one JPA metamodel must be present" with @WebMvcTest

I'm fairly new to Spring, trying to do some basic integration tests for a @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());
    }
}

but I'm getting

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!

Unlike most people posting this error, I don't want to use JPA for this. Am I trying to use @WebMvcTest incorrectly? How can I track down the Spring magic that's inviting JPA to this party?

Upvotes: 26

Views: 21580

Answers (5)

Mohnish
Mohnish

Reputation: 2045

Alternatively, you can define a custom configuration class inside your test case, including only the controller (plus its dependencies), to force Spring to use this context.
Please note, you'll still have access to MockMvc and other goodness in your test case, if it's WebMvcTest annotated.

@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 {}

Upvotes: 6

youhans
youhans

Reputation: 6839

Add @MockBean(JpaMetamodelMappingContext.class) to above of class DemoControllerIntegrationTests:

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

Because you have not used a database in your test, Spring throws this exception. By mocking JpaMetamodelMappingContext class you will bypass the needed metamodel.

Upvotes: 3

Nuñito Calzada
Nuñito Calzada

Reputation: 2056

Remove any @EnableJpaRepositories or @EntityScan from your SpringBootApplication class instead do this:

package com.tdk;

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

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

And put it in a separate config class:

package com.tdk.config;

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

}

And here the tests:

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

}

Upvotes: 17

Mohamed Ibrahim Elsayed
Mohamed Ibrahim Elsayed

Reputation: 2964

If anyone uses Spring boot and don't want to remove @EntityScan and @EnableJpaRepositories you can remove @WebMvcTest annotation from your test class and add the following instead:

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

    //...
}

and you will be able to autowire MockMvc and use it.

Upvotes: 2

Justin K
Justin K

Reputation: 318

I had the same problem. @WebMvcTest looks for a class annotated with @SpringBootApplication (in the same directory or higher up in your app structure if it doesn't find one). You can read how this works @ https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-mvc-tests.

If your class annotated with @SpringBootApplication also has @EntityScan /@EnableJpaRepositories this error occurs. Because you have these annotations with @SpringBootApplication and you are mocking the service ( so actually not using any JPA ). I found a workaround which may not be the prettiest, but works for me.

Place this class in your test directory ( the root ). @WebMvcTest will find this class before your actual Application class. In this class you don't have to add @EnableJpaRepositories/@EntityScan.

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

And your test will look the same.

@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
    }
}

Hope this helps!

Upvotes: 6

Related Questions