Spring架控制器单元测试示例

学习使用Junit 5mockito对给定的Spring rest控制器API进行单元测试。这项技术可以同时应用于Spring Boot和Spring MVC应用程序。

1.如何为休息控制器编写正确的单元测试

在为休息控制器方法编写junit测试时,请记住:

  • 单元测试应该仅测试部分代码(即,用控制器类编写的代码),因此我们将模拟在控制器类中注入和使用的所有依赖项。
  • 如果测试利用其他依赖项(例如数据库/网络),则它是集成测试而不是单元测试。
  • 我们不应使用任何Web服务器,否则它将使单元测试变慢。
  • 每个单元测试应独立于其他测试。
  • 根据定义,单元测试应该是快速的。

2.使用Junit 5和Mockito进行单元测试控制器

2.1。Maven依赖

首先包括所需的依赖项。我们在这里使用Spring Boot应用程序。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>

    <!-- exclude junit 4 -->

    <exclusions>
        <exclusion>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- Junit 5 -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-launcher</artifactId>
    <scope>test</scope>
</dependency>

<!-- Mockito extention -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <scope>test</scope>
</dependency>

2.2。REST控制器

这是Spring boot rest控制器,我们将为其编写单元测试。

  • 控制器对EmployeeDAO类具有持久性。
  • addEmployee() api需要使用访问请求上下文ServletUriComponentsBuilder
  • addEmployee() api使用ResponseEntity类返回HTTP状态和标头。
import java.net.URI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import com.how2codex.rest.dao.EmployeeDAO;
import com.how2codex.rest.model.Employee;
import com.how2codex.rest.model.Employees;

@RestController
@RequestMapping(path = "/employees")
public class EmployeeController 
{
    @Autowired
    private EmployeeDAO employeeDao;

    @GetMapping(path="/", produces = "application/json")
    public Employees getEmployees() 
    {
        return employeeDao.getAllEmployees();
    }
    
    @PostMapping(path= "/", consumes = "application/json", produces = "application/json")
    public ResponseEntity<Object> addEmployee(@RequestBody Employee employee) {       
                
        //add resource
        employeeDao.addEmployee(employee);
        
        //Create resource location
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                                    .path("/{id}")
                                    .buildAndExpand(employee.getId())
                                    .toUri();
        
        //Send location in response
        return ResponseEntity.created(location).build();
    }
}

2.3。单元测试

下面给出的测试类包含上述Spring Boot 支架控制器的单元测试。这个测试班:

  • 使用@Mock注解为EmployeeDAO依赖关系创建模拟对象。
  • 用于@InjectMocks创建EmployeeController类,还注入模拟的employeeDAO实例。
  • MockitoExtension初始化模拟并处理严格的存根。此扩展等效于我们的JUnit4 MockitoJUnitRunner的JUnit Jupiter 。
  • 使用JUnitPlatform是可选的。它允许junit 5测试与IDE一起运行,并构建支持JUnit 4但尚不直接支持JUnit Platform的系统。
  • MockHttpServletRequest RequestContextHolder提供被测试代码需要的请求上下文。
  • 使用org.mockito.Mockito.when()thenReturn()API模拟所需的行为。
  • 最后,使用junit 5声明来声明测试结果与预期结果。

阅读更多:Mockito注解– @ Mock,@ Spy,@ Captor,@ InjectMocks

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.how2codex.rest.controller.EmployeeController;
import com.how2codex.rest.dao.EmployeeDAO;
import com.how2codex.rest.model.Employee;
import com.how2codex.rest.model.Employees;

@ExtendWith(MockitoExtension.class)
@RunWith(JUnitPlatform.class)
public class EmployeeControllerTest 
{
    @InjectMocks
    EmployeeController employeeController;
    
    @Mock
    EmployeeDAO employeeDAO;
    
    @Test
    public void testAddEmployee() 
    {
        MockHttpServletRequest request = new MockHttpServletRequest();
        RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
        
        when(employeeDAO.addEmployee(any(Employee.class))).thenReturn(true);
        
        Employee employee = new Employee(1, "Lokesh", "Gupta", "mobtrack_service@163.com");
        ResponseEntity<Object> responseEntity = employeeController.addEmployee(employee);
        
        assertThat(responseEntity.getStatusCodeValue()).isEqualTo(201);
        assertThat(responseEntity.getHeaders().getLocation().getPath()).isEqualTo("/1");
    }
    
    @Test
    public void testFindAll() 
    {
        // given
        Employee employee1 = new Employee(1, "Lokesh", "Gupta", "mobtrack_service@163.com");
        Employee employee2 = new Employee(2, "Alex", "Gussin", "example@gmail.com");
        Employees employees = new Employees();
        employees.setEmployeeList(Arrays.asList(employee1, employee2));

        when(employeeDAO.getAllEmployees()).thenReturn(employees);

        // when
        Employees result = employeeController.getEmployees();

        // then
        assertThat(result.getEmployeeList().size()).isEqualTo(2);
        
        assertThat(result.getEmployeeList().get(0).getFirstName())
                        .isEqualTo(employee1.getFirstName());
        
        assertThat(result.getEmployeeList().get(1).getFirstName())
                        .isEqualTo(employee2.getFirstName());
    }
}

3.演示

在IDE中运行上述测试。我用过Eclipse。

Spring架控制器单元测试示例
Spring架控制器单元测试示例

4。结论

在这个带有Junit 5和mockito的Spring引导休息控制器单元测试示例中,我们学习了编写模拟所有控制器依赖项并且仅测试必要部分的测试。

我们还了解到,在进行单元测试时,我们不会使用实际的Web服务器来运行该应用程序。仅在集成测试时才需要服务器。

saigon has written 1445 articles

Leave a Reply