[英]Unit testing for spring boot RESTFUL Controller
我正在尝试对 spring boot RESTFUL 控制器进行单元测试,但我收到一个空指针异常。 该应用程序有一个StudentController
,它依赖于StudentService
。 这是控制器代码:
package com.demo.student.demo.controller;
import com.demo.student.demo.annotation.ApiDescription;
import com.demo.student.demo.entity.Student;
import com.demo.student.demo.entity.StudentDto;
import com.demo.student.demo.service.StudentService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping(value = "/v1/students")
@Api(description = "This is a general description for StudentController RESTFUL Controller")
public class StudentController {
private StudentService studentService;
private ModelMapper modelMapper;
public StudentController(ModelMapper modelMapper, StudentService studentService){
this.studentService = studentService;
this.modelMapper = modelMapper;
}
private StudentDto convertToDto(Student student) {
StudentDto studentDto = modelMapper.map(student, StudentDto.class);
return studentDto;
}
private Student convertToEntity(StudentDto studentDto) {
Student student = modelMapper.map(studentDto, Student.class);
return student;
}
@GetMapping
@ApiOperation("Returns a list of all students in the system.")
@ApiDescription("FIND_ALL_STUDENTS.md")
public List<StudentDto> findAll(){
List<Student> students = studentService.findAl();
return students.stream()
.map(student -> convertToDto(student))
.collect(Collectors.toList());
}
@GetMapping("/{id}")
@ApiOperation("Returns a specific Student by his/her identifier. 404 if does not exist.")
@ApiDescription("FIND_STUDENT_BY_ID.md")
public StudentDto findById(@PathVariable("id") long id){
return convertToDto(studentService.findById(id));
}
@PostMapping
@ApiOperation("Creates/Updates a new Student.")
@ApiDescription("SAVE_OR_UPDATE_STUDENT.md")
public StudentDto saveOrUpdate(@RequestBody StudentDto studentDto) {
Student student = convertToEntity(studentDto);
student.setUsername(student.getEmail() + "----" + student.getId());
Student studentCreated = studentService.saveOrUpdate(student);
return convertToDto(studentCreated);
}
@DeleteMapping("/{id}")
@ApiOperation("Deletes a Student from the system. 404 if the Student's identifier is not found.")
@ApiDescription("DELETE_STUDENT_BY_ID.md")
public void deleteById(@PathVariable("id") long id){
studentService.deleteById(id);
}
}
这是服务实现代码:
package com.demo.student.demo.service;
import com.demo.student.demo.entity.Student;
import com.demo.student.demo.exception.StudentNotFoundException;
import com.demo.student.demo.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class StudentServiceImpl implements StudentService {
private StudentRepository studentRepository;
@Autowired
public StudentServiceImpl(StudentRepository studentRepository){
this.studentRepository = studentRepository;
}
@Override
public List<Student> findAl() {
return studentRepository.findAll();
}
@Override
public Student findById(long id) {
return studentRepository.findById(id).orElseThrow(() -> new StudentNotFoundException(id));
}
@Override
public Student saveOrUpdate(Student student) {
studentRepository.save(student);
return student;
}
@Override
public void deleteById(long id) {
if(this.findById(id) != null)
studentRepository.deleteById(id);
}
}
这是控制器的测试类:
package com.demo.student.demo.controller;
import com.demo.student.demo.entity.Student;
import com.demo.student.demo.service.StudentService;
import com.demo.student.demo.service.StudentServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
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 java.util.Arrays;
import java.util.List;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(StudentController.class)
class StudentControllerTest {
private final static String URI = "/v1/students";
@Autowired
private MockMvc mockMvc;
@MockBean
private StudentService studentService;
@Test
void findAll() throws Exception {
// given
Student student = new Student(1, "test", "test@test.test");
List<Student> students = Arrays.asList(student);
given(studentService.findAl()).willReturn(students);
// when + then
mockMvc.perform(get(URI))
.andExpect(status().isOk())
.andExpect(content().json("[{'id':1,'email':'test@test.test'}]"));
}
}
这也是 pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.demo.student</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>0.7.5</version>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.22.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.5.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
当我运行测试时,我收到一个空指针异常,指向学生服务检索所有学生数据的StudentController
类中的代码行: List<Student> students = studentService.findAl();
java.lang.NullPointerException: null
at com.demo.student.demo.controller.StudentController.findAll
谁能告诉我问题出在哪里?
以这种方式注释您的测试类。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
自动装配这个:
@Autowired
private WebApplicationContext wac;
这只是一个类变量:
private MockMvc mockMvc;
您的设置方法:
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
在这一行之前:
given(studentService.findAl()).willReturn(students);
创造:
StudentService studentService = org.mockito.Mockito.mock(StudentService.class);
它应该运行。
你的测试有几件事,首先你混合了 JUnit4 和 JUnit5,不要,其次你试图对抗 Spring Boot 创建的模拟,不要。 最后,您正在为服务和注册行为创建一个新的模拟。
@RunWith
注释,该注释用于 JUnit4 测试,JUnit5 不需要。@BeforeEach
方法,因为它会干扰 Spring Boot 准备的MockMvc
(通过@WebMvcTest
)和@MockBean
创建的模拟。StudentService studentService = mock(StudentService.class);
从你的测试方法。 @MockBean
已经创建了一个模拟。 这将创建一个新的模拟,而不是绑定到控制器。@WebMvcTest
注释的@ExtendWith
(从 Spring Boot 2.1.x 开始)。 因此,还要添加它以使用正确的执行模型。 专业提示:从StudentController
中删除无参数构造函数和@Autowired
,Spring 足够聪明,可以选择单个构造函数。
最后我找到了一个答案,添加@ExtendWith(SpringExtension.class)
以便在 JUnit5 中工作。
所以,我的控制器测试类是:
package com.demo.student.demo.controller;
import com.demo.student.demo.entity.Student;
import com.demo.student.demo.service.StudentService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import java.util.List;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@ExtendWith(SpringExtension.class)
@WebMvcTest
class StudentControllerTest {
private final static String URI = "/v1/students";
@Autowired
private MockMvc mockMvc;
@MockBean
private StudentService studentService;
@Test
void findAll() throws Exception {
// given
Student student = new Student(1, "test", "test@test.test");
List<Student> students = Arrays.asList(student);
given(studentService.findAl()).willReturn(students);
// when + then
mockMvc.perform(get(URI))
.andExpect(status().isOk())
.andExpect(content().json("[{'id':1,'email':'test@test.test'}]"));
}
}
还有 pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.demo.student</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>0.7.5</version>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.22.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.5.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.