我试图用@MockBean
中的@WebMvcTest
和JUnit 5编写单元测试。不幸的是,看起来@MockBean
被忽略了,它试图建立完整的持久层,但是这失败了,而这并不是我想要的单元测试。
据我了解,@WebMvcTest
根本不应该触发应用程序上下文的设置。它应该用于单元测试。所以问题可能是为什么要创建一个应用程序上下文?
主类:
@SpringBootApplication
@EntityScan("ch.xxx.infop.common.entity")
@EnableJpaRepositories("ch.xxx.infop.dao")
public class ApplicationDispatch {
public static void main(String[] args) {
SpringApplication.run(ApplicationDispatch.class, args);
}
}
DispatchController:
@RestController
public class DispatchController {
@Autowired
VmRepository vmRepository;
@RequestMapping(path="/compress/{fpId}/{datenlieferantId}/{varianteTyp}")
public String publishMessage(
@PathVariable long fpId,
@PathVariable long datenlieferantId,
@PathVariable VarianteTyp varianteTyp) {
List<Vm> vmList = vmRepository.findByFpIdAndDatenlieferantId(fpId, datenlieferantId);
// ...
return "success";
}
}
DispatchUnitTest:
@ExtendWith(SpringExtension.class)
@WebMvcTest(DispatchController.class)
public class DispatchUnitTest {
@MockBean
private VmRepository vmRepository;
@Autowired
private MockMvc mvc;
@BeforeEach
public void setUp() {
List<Vm> testVmList = new ArrayList<>();
for (long i=0; i<3; i++) {
Vm vm = new Vm();
vm.setId(i);
testVmList.add(vm);
}
when(vmRepository.findByFpIdAndDatenlieferantId(anyLong(), anyLong())).thenReturn(testVmList);
}
@Test
public void contextLoads() throws Exception {
mvc.perform(
MockMvcRequestBuilders.get("/compress/2015/14/J"))
.andExpect(status().isOk())
.andExpect(content().string("success"));
}
}
测试输出:
10:40:57.169 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration ch.xxx.infop.dispatch.ApplicationDispatch for test class ch.xxx.infop.dispatch.DispatchUnitTest
10:40:57.171 [main] DEBUG org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper - @TestExecutionListeners is not present for class [ch.xxx.infop.dispatch.DispatchUnitTest]: using defaults.
10:40:57.171 [main] INFO org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
10:40:57.191 [main] INFO org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@11c9af63, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@757acd7b, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@36b4fe2a, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@574b560f, org.springframework.test.context.support.DirtiesContextTestExecutionListener@ba54932, org.springframework.test.context.transaction.TransactionalTestExecutionListener@28975c28, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@3943a2be, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@343570b7, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@157853da, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@71c3b41, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@236e3f4e, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@3cc1435c]
10:40:57.195 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@46fa7c39 testClass = DispatchUnitTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@1fb700ee testClass = DispatchUnitTest, locations = '{}', classes = '{class ch.xxx.infop.dispatch.ApplicationDispatch}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@4f67eb2a key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration, org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration, org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration, org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration, org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration, org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration, org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration, org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration, org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration, org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@1cbbffcd, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@3d51f06e, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@df16af6c, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@498d318c, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@d8cd8528, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@f44e2ec2, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@7e07db1f], resourceBasePath = '', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]], class annotated with @DirtiesContext [false] with mode [null].
10:40:57.342 [main] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper=true, server.port=-1}
,------. ,--. ,--. ,--.
| .-. \ `--' ,---. ,---. ,--,--. ,-' '-. ,---. | ,---.
| | \ : ,--. ( .-' | .-. | ' ,-. | '-. .-' | .--' | .-. |
| '--' / | | .-' `) | '-' ' \ '-' | | | \ `--. | | | |
`-------' `--' `----' | |-' `--`--' `--' `---' `--' `--'
`--'
2019-05-24 10:40:57.725 INFO 16844 --- [ main] ch.xxx.infop.dispatch.DispatchUnitTest : Starting DispatchUnitTest on K57176 with PID 16844 (started by ue73011 in C:\devxxx\projekte\infop-dispatch)
2019-05-24 10:40:57.727 INFO 16844 --- [ main] ch.xxx.infop.dispatch.DispatchUnitTest : No active profile set, falling back to default profiles: default
2019-05-24 10:40:58.248 INFO 16844 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2019-05-24 10:40:58.501 INFO 16844 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 238ms. Found 1 repository interfaces.
2019-05-24 10:40:59.589 INFO 16844 --- [ main] faultConfiguringBeanFactoryPostProcessor : No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.
2019-05-24 10:40:59.597 INFO 16844 --- [ main] faultConfiguringBeanFactoryPostProcessor : No bean named 'taskScheduler' has been explicitly defined. Therefore, a default ThreadPoolTaskScheduler will be created.
2019-05-24 10:40:59.604 INFO 16844 --- [ main] faultConfiguringBeanFactoryPostProcessor : No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
2019-05-24 10:40:59.670 INFO 16844 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationDisposableAutoCreatedBeans' of type [org.springframework.integration.config.annotation.Disposables] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-05-24 10:40:59.702 INFO 16844 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.hateoas.config.HateoasConfiguration' of type [org.springframework.hateoas.config.HateoasConfiguration$$EnhancerBySpringCGLIB$$82ead0f5] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-05-24 10:41:00.624 WARN 16844 --- [ main] o.s.w.c.s.GenericWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'vmRepositoryImpl': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManagerFactory' available
2019-05-24 10:41:00.630 INFO 16844 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-05-24 10:41:00.661 ERROR 16844 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean of type 'javax.persistence.EntityManagerFactory' that could not be found.
Action:
Consider defining a bean of type 'javax.persistence.EntityManagerFactory' in your configuration.
2019-05-24 10:41:00.667 ERROR 16844 --- [ main] o.s.test.context.TestContextManager : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@36b4fe2a] to prepare test instance [ch.xxx.infop.dispatch.DispatchUnitTest@6ed043d3]
最佳答案
经过大量的搜索和尝试并出错后,我找到了以下解决方案:
@SpringJUnitConfig
@WebMvcTest(DispatchController.class)
public class DispatchUnitTest {
@Configuration
static class Config {
@Bean
DispatchController dispatchController() {
return new DispatchController();
}
}
@MockBean
private VmRepository vmRepository;
@Autowired
private MockMvc mvc;
@BeforeEach
public void setUp() {
List<Vm> testVmList = new ArrayList<>();
for (long i=0; i<3; i++) {
Vm vm = new Vm();
vm.setId(i);
testVmList.add(vm);
}
when(vmRepository.findByFpIdAndDatenlieferantId(anyLong(), anyLong())).thenReturn(testVmList);
}
@Test
public void contextLoads() throws Exception {
mvc.perform(
get("/compress/2015/14/J"))
.andExpect(status().isOk())
.andExpect(content().string("success"));
}
}
主要更改是使用
@SpringJUnitConfig
而不是@ExtendWith(SpringExtension.class)
。这将添加@ContextConfiguration
,即自动检测@Configuration
static class Config {
@Bean
DispatchController dispatchController() {
return new DispatchController();
}
}
我不完全理解为什么这样做是必要的,与我的问题代码的主要区别是什么。实际上,我发现了很多没有这种配置的示例。
因此,在这个问题上仍有获得荣耀的空间;-)。