Reputation: 27886
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
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
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
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
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
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