我试图用@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();
    }
}

我不完全理解为什么这样做是必要的,与我的问题代码的主要区别是什么。实际上,我发现了很多没有这种配置的示例。

因此,在这个问题上仍有获得荣耀的空间;-)。

09-11 20:49