jakarta.servlet.ServletException, different behaviour between run, test and http request with SpringBoot API

huangapple go评论65阅读模式
英文:

jakarta.servlet.ServletException, different behaviour between run, test and http request with SpringBoot API

问题

我正在构建一个SpringBoot API来学习这个框架,我面临两个奇怪的问题,它们可能以某种方式相互关联。

第一个问题是,当我尝试使用自己的Junit测试类EmployeeControllerTest测试我的代码时,调用带有HTTP请求的方法会返回以下错误:
jakarta.servlet.ServletException: Request processing failed: java.util.NoSuchElementException: No value present

第二个问题是,当我使用Postman执行这些测试时,返回员工列表的/employees请求正常工作,但是/employee请求(无论是否在URL中添加了id),API都没有返回任何内容。

除此之外,从代码内部调用方法(在运行类中)也能正常工作,我得到了需要的每个结果。

这里是涉及的每个部分的代码。首先是模型类:

// 在上面的代码中

然后是存储库类:

// 在上面的代码中

接下来是服务类:

// 在上面的代码中

最后是控制器类:

// 在上面的代码中

此外,主类和测试类:

// 在上面的代码中
// 在上面的代码中

附加信息,构建数据库时使用的SQL脚本:

// 在上面的代码中
英文:

I am building a SpringBoot API to learn the framework and I am facing two curious problems which probably are linked in some way.

First problem, when I try to test my code with my own Junit test class called EmployeeControllerTest, calling the method with http request returns the following error :
jakarta.servlet.ServletException: Request processing failed: java.util.NoSuchElementException: No value present

Second problem, when I perform those tests with Postman, the request /employees returning the list of employees works perfectly but the request /employee (with or without id added to the url), the API returns nothing.

In addition to this, calling the method from inside the code (in the run class) works great, I have every result I need.

Here are the code of every part involved. First the model class :

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;

}

The repository class :


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> {

}

The service class :

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;
    }

}

and the controller class :

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;

    // Read - Get all employees

    // @return - An Iterable object of Employee full filled

    @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;
        }
    }
}

Additionnaly, the main and test classes :

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());
	}

}
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 java.io.PrintStream;

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.AutoConfigureWebMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
//import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.ResultHandler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.openclassrooms.api.controller.EmployeeController;
import com.openclassrooms.api.models.Employee;
import com.openclassrooms.api.service.EmployeeService;

//@SpringBootTest
//@AutoConfigureWebMvc
@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"));

                //.andExpect(jsonPath("$[0].firstName", is("Laurent")));
    }
}

Thanks in advance for any answer !

EDIT : the SQL script used when building the database :

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-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
  (&#39;Laurent&#39;, &#39;GINA&#39;, &#39;laurentgina@mail.com&#39;, &#39;laurent&#39;),
  (&#39;Sophie&#39;, &#39;FONCEK&#39;, &#39;sophiefoncek@mail.com&#39;, &#39;sophie&#39;),
  (&#39;Agathe&#39;, &#39;FEELING&#39;, &#39;agathefeeling@mail.com&#39;, &#39;agathe&#39;);

<!-- end snippet -->

答案1

得分: 1

代码中存在一些问题。

首先,在EmployeeController类的getEmployee方法中,if条件检查employeeService返回的Optional是否为空,但如果它为空,代码返回null,这不是期望的行为。相反,您应该检查Optional是否存在,如果存在,则返回该值,否则返回适当的响应,指示未找到员工。

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

    if (emp.isPresent()) {
        Employee employee = emp.get();
        System.println(employee.getFirstName());
        return employee;
    } else {
        System.out.println("ABSENT");
        // 返回适当的响应,指示未找到员工
        return null;
    }
}

相同的问题也适用于没有路径变量的getEmployee方法。

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

    if (emp.isPresent()) {
        Employee employee = emp.get();
        System.out.println(employee.getFirstName());
        return employee;
    } else {
        System.out.println("ABSENT");
        // 返回适当的响应,指示未找到员工
        return null;
    }
}

关于JUnit测试类的问题,如果没有更多信息,例如确切的错误消息或测试类的代码片段,很难确定问题。总的来说,代码需要更强大地处理未找到员工的情况,而测试类需要进一步调查以确定问题的根本原因。

英文:

There seems to be a couple of issues with the code.

First, in the getEmployee method of the EmployeeController class, the if condition checks if the Optional<Employee> returned by the employeeService is empty, but if it's empty, the code returns null, which is not the desired behavior. Instead, you should check if the Optional is present, and if it is, return the value, otherwise return an appropriate response indicating that the employee was not found.

@GetMapping(&quot;/employee/{id}&quot;)
public Employee getEmployee(@PathVariable(&quot;id&quot;) final Long id) {
    Optional&lt;Employee&gt; emp = employeeService.getEmployee(id);

    if (emp.isPresent()) {
        Employee employe = emp.get();
        System.out.println(employe.getFirstName());
        return employe;
    } else {
        System.out.println(&quot;ABSENT&quot;);
        // return an appropriate response indicating that the employee was not found
        return null;
    }
}

The same issue applies to the getEmployee method without a path variable.

@GetMapping(&quot;/employee&quot;)
public Employee getEmployee() {
    Optional&lt;Employee&gt; emp = employeeService.getEmployee(1L);

    if (emp.isPresent()) {
        Employee employe = emp.get();
        System.out.println(employe.getFirstName());
        return employe;
    } else {
        System.out.println(&quot;ABSENT&quot;);
        // return an appropriate response indicating that the employee was not found
        return null;
    }
}

Regarding the issue with the Junit test class, it's difficult to determine the problem without more information, such as the exact error message or a code snippet of the test class.

Overall, the code needs to be more robust in handling cases where the employee was not found, and the test class needs to be further investigated to determine the root cause of the issue.

huangapple
  • 本文由 发表于 2023年2月6日 17:23:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/75359418.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定