You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot API运行、测试与HTTP请求行为不一致问题排查

SpringBoot API 异常问题排查

问题描述

  • 自定义JUnit测试类EmployeeControllerTest发起HTTP请求时,抛出异常:jakarta.servlet.ServletException: Request processing failed: java.util.NoSuchElementException: No value present
  • Postman测试时,/employees接口正常返回员工列表,但/employee(带或不带id参数)接口无任何返回
  • 代码内部(主类CommandLineRunner中)调用相关方法可正常获取预期结果

相关代码

模型类

package com.openclassrooms.api.models;

import jakarta.persistence.*;
import lombok.Data;

@Data
@Entity
@Table(name = "employees")
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    private String mail;

    private String password;
}

仓库类

package com.openclassrooms.api.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.openclassrooms.api.models.Employee;

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
}

服务类

package com.openclassrooms.api.service;

import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.openclassrooms.api.models.Employee;
import com.openclassrooms.api.repository.EmployeeRepository;

@Service
public class EmployeeService {

    @Autowired
    private EmployeeRepository employeeRepository;

    public Optional<Employee> getEmployee(final Long id) {
        System.out.println("getEmployee ok");
        return employeeRepository.findById(id);
    }

    public Iterable<Employee> getEmployees() {
        System.out.println("getEmployees ok");
        return employeeRepository.findAll();
    }

    public void deleteEmployee(final Long id) {
        employeeRepository.deleteById(id);
    }

    public Employee saveEmployee(Employee employee) {
        Employee savedEmployee = employeeRepository.save(employee);
        return savedEmployee;
    }
}

原控制器类(存在逻辑错误)

package com.openclassrooms.api.controller;

import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.openclassrooms.api.models.Employee;
import com.openclassrooms.api.service.EmployeeService;

@RestController
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;

    @GetMapping("/employees")
    public Iterable<Employee> getEmployees() {
        Iterable<Employee> list = employeeService.getEmployees();
        System.out.println(list);
        return list;
    }

    @GetMapping("/employee/{id}")
    public Employee getEmployee(@PathVariable("id") final Long id) {
        Optional<Employee> emp = employeeService.getEmployee(id);

        if (emp.isEmpty()) {
            Employee employe = emp.get();
            System.out.println(employe.getFirstName());
            return employe;
        } else {
            System.out.println("ABSENT");
            return null;
        }
    }

    @GetMapping("/employee")
    public Employee getEmployee() {
        Optional<Employee> emp = employeeService.getEmployee(1L);

        if (emp.isEmpty()) {
            Employee employe = emp.get();
            System.out.println(employe.getFirstName());
            return employe;
        } else {
            System.out.println("ABSENT");
            return null;
        }
    }
}

主类

package com.openclassrooms.api;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.openclassrooms.api.models.Employee;
import com.openclassrooms.api.service.EmployeeService;

@SpringBootApplication
public class ApiApplication implements CommandLineRunner {

    @Autowired
    private EmployeeService employeeService;

    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
    }

    public void run(String... args) throws Exception {
        if (employeeService.getEmployee(1L).isPresent()) {
            Employee emp1 = employeeService.getEmployee(1L).get();
            System.out.println(emp1.getFirstName() + " " + emp1.getLastName());
        } else {
            System.out.println("Erreur, employé absent.");
        }
        System.out.println(employeeService.getEmployees());
    }
}

原测试类(未模拟Service返回值)

package com.openclassrooms.api;

import static org.hamcrest.CoreMatchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;

import com.openclassrooms.api.controller.EmployeeController;
import com.openclassrooms.api.models.Employee;
import com.openclassrooms.api.service.EmployeeService;

@WebMvcTest(controllers = EmployeeController.class)
public class EmployeeControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private EmployeeService employeeService;

    @Test
    public void testGetEmployees() throws Exception {
        Employee response = new Employee();

        mockMvc.perform(get("/employee"))
                .andExpect(status().isOk())
                .andDo(print(System.out))
                .andExpect(jsonPath("$.firstName").value("Laurent"));
    }
}

数据库初始化SQL脚本

DROP TABLE IF EXISTS employees;
 
CREATE TABLE employees (
  id INT AUTO_INCREMENT  PRIMARY KEY,
  first_name VARCHAR(250) NOT NULL,
  last_name VARCHAR(250) NOT NULL,
  mail VARCHAR(250) NOT NULL,
  password VARCHAR(250) NOT NULL
)
 
INSERT INTO employees (first_name, last_name, mail, password) VALUES
  ('Laurent', 'GINA', 'laurentgina@mail.com', 'laurent'),
  ('Sophie', 'FONCEK', 'sophiefoncek@mail.com', 'sophie'),
  ('Agathe', 'FEELING', 'agathefeeling@mail.com', 'agathe');

问题根源与修复方案

1. 控制器逻辑完全写反(核心问题)

原控制器中getEmployee方法的条件判断颠倒:当emp.isEmpty()为true(无数据)时调用emp.get(),直接触发NoSuchElementException;有数据时反而返回null,导致接口无内容返回。

修正后的控制器代码

package com.openclassrooms.api.controller;

import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;

import com.openclassrooms.api.models.Employee;
import com.openclassrooms.api.service.EmployeeService;

@RestController
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;

    @GetMapping("/employees")
    public Iterable<Employee> getEmployees() {
        Iterable<Employee> list = employeeService.getEmployees();
        System.out.println(list);
        return list;
    }

    @GetMapping("/employee/{id}")
    public Employee getEmployee(@PathVariable("id") final Long id) {
        Optional<Employee> emp = employeeService.getEmployee(id);

        // 修正判断逻辑:存在数据则返回,不存在抛出404异常
        if (emp.isPresent()) {
            Employee employe = emp.get();
            System.out.println(employe.getFirstName());
            return employe;
        } else {
            System.out.println("ABSENT");
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Employee not found with id: " + id);
        }
    }

    @GetMapping("/employee")
    public Employee getEmployee() {
        Optional<Employee> emp = employeeService.getEmployee(1L);

        if (emp.isPresent()) {
            Employee employe = emp.get();
            System.out.println(employe.getFirstName());
            return employe;
        } else {
            System.out.println("ABSENT");
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Employee not found with id: 1");
        }
    }
}

2. JUnit测试未模拟Service返回值

@WebMvcTest隔离控制器层,@MockBean的EmployeeService默认返回空Optional,触发控制器异常。需用Mockito模拟Service的返回数据。

修正后的测试类代码

package com.openclassrooms.api;

import static org.hamcrest.CoreMatchers.is;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;

import com.openclassrooms.api.controller.EmployeeController;
import com.openclassrooms.api.models.Employee;
import com.openclassrooms.api.service.EmployeeService;
import java.util.Optional;

@WebMvcTest(controllers = EmployeeController.class)
public class EmployeeControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private EmployeeService employeeService;

    @Test
    public void testGetEmployee() throws Exception {
        // 模拟Service返回id=1的员工数据
        Employee mockEmployee = new Employee();
        mockEmployee.setId(1L);
        mockEmployee.setFirstName("Laurent");
        mockEmployee.setLastName("GINA");
        mockEmployee.setMail("laurentgina@mail.com");
        mockEmployee.setPassword("laurent");

        when(employeeService.getEmployee(1L)).thenReturn(Optional.of(mockEmployee));

        mockMvc.perform(get("/employee"))
                .andExpect(status().isOk())
                .andDo(print())
                .andExpect(jsonPath("$.firstName").value("Laurent"));
    }
}

内容的提问来源于stack exchange,提问作者Bhorah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 18:05:29