@MockBean 不适用于带有 JUnit 5 和 Spring Boot 2 的 @WebMvcTest?

Posted

技术标签:

【中文标题】@MockBean 不适用于带有 JUnit 5 和 Spring Boot 2 的 @WebMvcTest?【英文标题】:@MockBean doesn't work for @WebMvcTest with JUnit 5 and Sping Boot 2? 【发布时间】:2019-09-07 16:50:00 【问题描述】:

我在 *** 上研究了类似的问题,但在我的案例中找不到根本原因。

上下文:

我在SpringBoot 2 中有@RestController,依赖于BookSearcherService。我想模拟 BookSearcherService 以便对控制器进行单元测试。

错误:

org.springframework.web.util.NestedServletException: 请求 处理失败;嵌套异常是 java.lang.NullPointerException 在 org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1013) 在 org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:634) 在 org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) 在 org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:71) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:741) 在 org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:166) 在 org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133) 在 org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 在 org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133) 在 org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 在 org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133) 在 org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 在 org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133) 在 org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:182) 在 com.search.book.Booksearcher.controller.BookSearcherControllerMockMvcTest.canListAll(BookSearcherControllerMockMvcTest.java:54) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:498) 在 org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:532) 在 org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115) 在 org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:171) 在 org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) 在 org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:167) 在 org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:114) 在 org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:108) 在 org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74) 在 java.util.ArrayList.forEach(ArrayList.java:1257) 在 org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112) 在 org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74) 在 java.util.ArrayList.forEach(ArrayList.java:1257) 在 org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112) 在 org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98) 在 org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74) 在 org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) 在 org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) 在 org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) 在 org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220) 在 org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188) 在 org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202) 在 org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181) 在 org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) 在 org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:89) 在 org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41) 在 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541) 在 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763) 在 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463) 在 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209) 引起:java.lang.NullPointerException at com.search.book.Booksearcher.controller.BookSearcherController.listAll(BookSearcherController.java:31) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:498) 在 org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189) 在 org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) 在 org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) 在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) 在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) 在 org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) 在 org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038) 在 org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) 在 org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ... 59 更多

代码:

BookSearcherController.java:

@RestController
@RequestMapping("/book")
@RequiredArgsConstructor
public class BookSearcherController 

    private BookSearcherService bookSearcherService;

    @GetMapping(path = "/list", produces =  "application/json" )
    @ResponseBody
    public List<BookDto> listAll() 
        return bookSearcherService.listAll();
    

BookSearcherService.java:

@Service
public class BookSearcherService 

    private BookSearcherCrudService bookSearcherCrudService;

    @Autowired
    void setBookSearcherRepository(BookSearcherCrudService bookSearcherCrudService) 
        this.bookSearcherCrudService = bookSearcherCrudService;
    

    private BookSearcherRepository bookSearcherRepository;

    @Autowired
    void setBookSearcherRepository(BookSearcherRepository bookSearcherRepository) 
        this.bookSearcherRepository = bookSearcherRepository;
    

    public List<BookDto> listAll() 
        List<Book> books = bookSearcherRepository.findAll();
        return books.stream()
                .map(bookSearcherCrudService::toDto)
                .collect(Collectors.toList());
    

BookSearcherControllerMockMvcTest.java:

@WebMvcTest(controllers = BookSearcherController.class)
public class BookSearcherControllerMockMvcTest 

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private BookSearcherService bookSearcherService;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    public void canListAll() throws Exception 
        assertThat(bookSearcherService).isNotNull();
        assertThat(mockMvc).isNotNull();

        List<BookDto> books = asList(new BookDto("Title 1"), new BookDto("Title 2"), new BookDto("Title 3"));

        given(bookSearcherService.listAll()).willReturn(books);

        ResultActions perform = mockMvc.perform(get("/book/list"));
    

POM.XML

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.search.book</groupId>
    <artifactId>book-searcher</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>book-searcher</name>
    <description>Project for books search</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.9.5</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-launcher</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-runner</artifactId>
            <scope>test</scope>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

问题:

如上所述的问题是BookSearcherService 在对mockMvc 执行调用时具有null。这意味着要么@MockBean 不起作用,要么我犯了一个几个小时都无法发现的错误。我正在使用Java8

【问题讨论】:

问题可能是bookSearcherService没有被注入BookSearchController,因为它使用了@RequiredArgsConstructor,它没有添加@Autowired注解。 @ArhoHuttunen 有帮助。 很好,我会改为回答。 【参考方案1】:

问题在于bookSearcherService 没有被注入到BookSearchController 中,因为它使用了@RequiredArgsConstructor,在这种情况下不会创建正确的构造函数。

@RequiredArgsConstructor 文档指出,将使用未初始化的 final@NonNull 字段的参数创建构造函数。但是,bookSearcherService 两者都不是,因此它没有作为构造函数参数添加。

因此,尽管 mock 本身不为 null,但它永远不会注入到测试的控制器中。

【讨论】:

我确信@RequiredArgsConstructor 可以完成@Autowired 的工作。谢谢。 它应该作为一个单一的构造函数,从 spring 4.2 或 4.3 iirc 开始尝试,有点奇怪 其实你是对的,但是@RequiredArgsConstructor在这里工作正常吗,因为bookSearcherService既不是final也不是@NonNull 我已更改答案以反映这一点。

以上是关于@MockBean 不适用于带有 JUnit 5 和 Spring Boot 2 的 @WebMvcTest?的主要内容,如果未能解决你的问题,请参考以下文章

Junit 测试不适用于 Drools 5.4.0.Final 和 JDK 8

事务不适用于 Spring 3.1 – H2 – junit 4 – hibernate 3.2

ContextConfiguration不适用于测试平台的JUnit4类

为啥@MockBean ConversionService 会导致错误

SpringJunitTest

带有 vue 2 组件的 vue 路由器不适用于 laravel 5.8