Spring Boot的 Mockito测试中的未满足的依赖异常

后端开发 2026-07-09

我在学习Mockito进行单元测试时,遇到了一个错误

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.codefinity.com.codefinity.firstrestapibooks.controller.BookControllerTest': Unsatisfied dependency expressed through field 'objectMapper': No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:767)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:748)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:146)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1446)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:402)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:157)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:111)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260)
    at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:242)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:186)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:214)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:197)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:214)
    at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1716)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:570)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
    at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:153)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:176)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265)
    at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:632)
    at java.base/java.util.Optional.orElseGet(Optional.java:364)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:2297)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1725)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1651)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:764)
    ... 23 more

我在测试一个控制器,代码在这里

package org.codefinity.com.codefinity.firstrestapibooks.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.codefinity.com.codefinity.firstrestapibooks.DTO.BookRequestDTO;
import org.codefinity.com.codefinity.firstrestapibooks.DTO.BookResponseDTO;
import org.codefinity.com.codefinity.firstrestapibooks.model.Book;
import org.codefinity.com.codefinity.firstrestapibooks.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.List;


@RestController
@RequestMapping("/books")
@Tag(name="Book",description = "API for managing books")
public class BookController {

    private final BookService bookService;

    public BookController(BookService bookService){
        this.bookService = bookService;
    }


    @GetMapping
    @Operation(summary="Get the list of all books")
    @ApiResponse(responseCode = "200", description = "books successfully retrieved")
    public List<BookResponseDTO> findAllBooks(){

        return bookService.findAllBooks();
    }

    @PostMapping
    @Operation(summary = "Create a new book")
    @ApiResponse(responseCode = "200", description = "Book Sucessfully created")
    public BookResponseDTO createBook(@RequestBody BookRequestDTO book){

        return bookService.createBook(book);
    }
    @PutMapping("/{id}")
    @Operation(summary = "Update and existing book by ID")
    @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Book succesfully updated"),
    @ApiResponse(responseCode = "404", description = "Id not found")} )
    public BookResponseDTO updateBook(@Parameter(description = "Id of book to be updated")@PathVariable String id, @RequestBody BookRequestDTO book){
        return bookService.updateBook(id, book);
    }

    @DeleteMapping("/{id}")
    @Operation(summary = "Delets a book by its ID")
    @ApiResponse(responseCode = "200", description = "id of book to be deleted")
    public void deleteBook(@Parameter(description = "id of the book to be deleted",required = true) @PathVariable String id){
        bookService.deleteBook(id);
    }
    @GetMapping("/author/{author}")
    public List<BookResponseDTO>findByAuthor(@PathVariable String author){
      return bookService.findBookByAuthor(author);
    }
}

我在测试update updateBook方法,测试代码在这里

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest
public class BookControllerTest {

    @MockitoBean
    private BookServiceImpl bookService;

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    private BookRequestDTO bookRequestDTO;

    private BookResponseDTO bookResponseDTO;
    @BeforeEach
    void setUp() {
        bookRequestDTO = new BookRequestDTO();
        bookRequestDTO.setAuthor("authorTest");
        bookRequestDTO.setName("nameTest");
        bookRequestDTO.setPrice("priceTest");

        bookResponseDTO = new BookResponseDTO();
        bookResponseDTO.setId("1");
        bookResponseDTO.setAuthor("authorTest");
        bookResponseDTO.setName("nameTest");
        bookResponseDTO.setPrice("priceTest");

    }

    @Test
    void testUpdateBook_whenBookExists_shouldReturnUpdateBook() throws Exception{
        String bookId="1";
      when(bookService.updateBook(bookId, bookRequestDTO)).thenReturn(bookResponseDTO);

      mockMvc.perform(put("/books/{id}",bookId)
              .contentType(MediaType.APPLICATION_JSON)
              .content(objectMapper.writeValueAsString(bookRequestDTO)))
              .andExpect(status().isOk())
              .andExpect(jsonPath("$.id").value(bookResponseDTO.getId()))
              .andExpect(jsonPath("$.name").value(bookResponseDTO.getName()))
              .andExpect(jsonPath("$.author").value(bookResponseDTO.getAuthor()))
              .andExpect(jsonPath("$.price").value(bookResponseDTO.getPrice()));
      verify(bookService).updateBook(bookId, bookRequestDTO);
    }
}

我遇到的主要问题是这行.content(objectMapper.writeValueAsString(bookRequestDTO))

应用程序在使用被 @Autowired注解的ObjectMapper时出现问题。我尝试将其切换为 @MockitoBean,但仍然出现如下错误

java.lang.NullPointerException: Cannot invoke "String.getBytes(java.nio.charset.Charset)" because "content" is null

    at org.springframework.test.web.servlet.request.AbstractMockHttpServletRequestBuilder.content(AbstractMockHttpServletRequestBuilder.java:301)
    at org.codefinity.com.codefinity.firstrestapibooks.controller.BookControllerTest.testUpdateBook_whenBookExists_shouldReturnUpdateBook(BookControllerTest.java:64)

我不确定问题出在哪里。下面是我的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 https://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>4.0.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.codefinity</groupId>
    <artifactId>first-rest-api-books</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>com.codefinity.firstrestapiooks</name>
    <description>com.codefinity.firstrestapiooks</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>25</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webmvc</artifactId>
            <version>4.1.0-RC1</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>3.0.3</version>
            <scope>compile</scope>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>4.1.0-RC1</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webmvc-test</artifactId>
            <version>4.1.0-RC1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>9.7.0</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.modelmapper</groupId>
            <artifactId>modelmapper</artifactId>
            <version>3.2.6</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-mysql</artifactId>
            <version>12.4.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.43.0</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>6.1.0-RC1</version>
        <scope>test</scope>
    </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>5.23.0</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.20.0</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-flyway</artifactId>
            <version>4.1.0-RC1</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>7.4.0.Final</version>
            <type>pom</type>
            <scope>compile</scope>
        </dependency>

        <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.46</version>
        <scope>compile</scope>
    </dependency>

    </dependencies>

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

</project>

我会在这里再补充BookRequestDTO和 BookResponseDTO,以防万一

package org.codefinity.com.codefinity.firstrestapibooks.DTO;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;

@Data
public class BookRequestDTO {
    private String name;
    private String author;
    private String price;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}
package org.codefinity.com.codefinity.firstrestapibooks.DTO;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;

@Data
public class BookResponseDTO {
    private String id;
    private String name;
    private String author;
    private String price;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }
}

请告诉我有哪些解答,谢谢。

解决方案

你现在使用的是Spring Boot 4,它将Jackson 3作为默认的JSON库,包名从 com.fasterxml.jackson 改为 tools.jackson

你的测试自动装配了 ObjectMapper(Jackson 2),但上下文中自动配置的bean现在是 JsonMapper(Jackson 3)。该类型在bean中不存在,因此会报错。

修复自动装配字段:

java

@Autowired
private JsonMapper objectMapper;
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章