Spring Boot CRUD REST API + Spring Data JPA + H2 DB:在测试中获取 NPE 但在 Postman 中不获取

Spring Boot CRUD REST API + Spring Data JPA + H2 DB: Getting NPE on tests but not on Postman

我正在做一些练习以熟悉 spring 框架并使用 REST API。我确实通过了练习,但没有通过我最初的实施。我想知道为什么我在测试中得到 NPE 而在 Postman 中却没有。

尝试解决此测验时测试抛出了 NPE:

{
  "title": "Math4",
  "text": "Which of the following is equal to 4?",
  "options": ["2*3", "5*8", "8*0", "1*5"],
  "answer": null
}

当我使用 Postman post 此测验到 dB 并解决时,一切正常。

但是当我 运行 它通过测试时,我收到以下信息:

堆栈跟踪:

java.lang.NullPointerException: null
at engine.presentation.controllers.QuizController.solveQuiz(QuizController.java:44) ~[main/:na]
at jdk.internal.reflect.GeneratedMethodAccessor90.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at jdk.internal.reflect.GeneratedMethodAccessor64.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:170) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:225) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access[=14=]0(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.29.jar:9.0.29]
at jdk.internal.reflect.GeneratedMethodAccessor47.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:191) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access[=14=]0(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at jdk.internal.reflect.GeneratedMethodAccessor47.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:191) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access[=14=]0(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at jdk.internal.reflect.GeneratedMethodAccessor47.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:191) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access[=14=]0(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) ~[spring-boot-actuator-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at jdk.internal.reflect.GeneratedMethodAccessor47.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:191) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access[=14=]0(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at jdk.internal.reflect.GeneratedMethodAccessor47.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:282) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.run(SecurityUtil.java:279) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at java.base/javax.security.auth.Subject.doAsPrivileged(Subject.java:550) ~[na:na]
at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:191) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.access[=14=]0(ApplicationFilterChain.java:47) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:149) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.run(ApplicationFilterChain.java:145) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.security.AccessController.doPrivileged(Native Method) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]

build.gradle

plugins {
    id 'org.springframework.boot' version '2.2.2.RELEASE'
    id 'java'
    id "io.freefair.lombok" version "6.1.0-m3"
}

apply plugin: 'io.spring.dependency-management'

sourceCompatibility = 11

repositories {
    mavenCentral()
}

sourceSets.main.resources.srcDirs = ["src/resources"]

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
}

application.settings

server.port=8889
management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true

# H2 data source setup
spring.datasource.url=jdbc:h2:file:../quizdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect


# Automatically update tables when persistence objects have changed
spring.jpa.hibernate.ddl-auto=update

# Setup for the H2 console, used for viewing data in the database
spring.h2.console.enabled=true
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false

# Show SQL statements generated by Spring ORM
spring.jpa.show-sql=true

Quiz.java:

@Entity
@Table(name = "quizzes")
@NoArgsConstructor
@Getter
@Setter
@ToString
@JsonPropertyOrder({
        "id",
        "title",
        "text",
        "options"
})
public class Quiz {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    private int id;

    @NotBlank(message = "Title is required")
    private String title;

    @NotBlank(message = "Text is required")
    private String text;

    @NotEmpty(message = "There must be at least 2 options")
    @Size(min = 2, message = "There must be at least 2 options")
    private LinkedHashSet<String> options;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private HashSet<Integer> answer;

    public void setAnswer(HashSet<Integer> answer) {
        this.answer = Optional.ofNullable(answer).orElseGet(HashSet::new);
    }
}

QuizController.java

@RestController
@RequestMapping("/api")
public class QuizController {

    private final QuizService quizService;

    @Autowired
    public QuizController(QuizService quizService) {
        this.quizService = quizService;
    }

    @GetMapping("/quizzes")
    public Iterable<Quiz> getQuizzes() {
        return quizService.findAll();
    }

    @GetMapping("/quizzes/{id}")
    public Quiz getQuiz(@PathVariable @Min(0) int id) {
        return quizService.findById(id);
    }

    @PostMapping("/quizzes")
    public Quiz saveQuiz(@RequestBody @Valid Quiz quiz) {
        return quizService.save(quiz);
    }

    @PostMapping("/quizzes/{id}/solve")
    public Response solveQuiz(@RequestBody @Valid AnswerDto answer,
                          @PathVariable @Min(0) int id) {
        return Response.builder()
                .success(quizService.findById(id).getAnswer().equals(answer.getAnswer()))
                .build();
    }
}

QuizService.java:

    @Service
public class QuizService {
    private final QuizRepository repository;

    @Autowired
    public QuizService(QuizRepository repository) {
        this.repository = repository;
    }

    public Iterable<Quiz> findAll() {
        return repository.findAll();
    }

    public Quiz findById(int id) {
        Optional<Quiz> quiz = repository.findById(id);

        if (quiz.isEmpty()) {
            throw new QuizNotFoundException(id);
        }

        return quiz.get();
    }

    public Quiz save(Quiz quiz) {
        return repository.save(quiz);
    }
}

我不明白的是,如果 setAnswer 方法从未将字段设置为空,那么如何使用空答案字段创建测验。据我了解,如有错误请指正。当使用@RequestBody 将JSON 反序列化为对象时,它使用默认(无参数)构造函数和定义的setter 方法来实例化对象。另外,为什么我无法在 Postman 上复制问题?

这仅用于教育目的。即使我解决了问题,我还是想了解我从哪里开始做错了。非常感谢任何帮助。

编辑:为澄清起见,如果在创建 Quiz 对象时将空值作为答案传递,则会创建一个空的 HashSet。这应该反序列化为一个空数组,如上面的 postman 图像所示。当我 post 回答测验时,我收到了成功响应,因为答案是 [];

问题是
quizService.findById(id).getAnswer().equals(answer.getAnswer())

在第一个 getAnswer() returns null 时失败,无法在其上调用 equals

您可以改用 null-safe Objects::equals

Objects.equals(quizService.findById(id).getAnswer(), answer.getAnswer())

问题是测试发送的测验 json 没有明确的 answer 字段。在那种情况下,您的 setter 不会被 Jackson 调用。要强制执行当前在 setter 中的逻辑,具有 @JsonCreator 的构造函数应该有所帮助。