基于 mockMvc 的 Controller 层单元测试

场景

单元测试是个让人很纠结的东东,dealline 催的很紧,不想写单测,但当项目复杂到一定程度需要重构时,返现没有单测,不敢随便改代码,生怕”敲一锤子,倒一大片“,但此时再补,已经有点晚了。磨刀不误砍柴功,这会偷的懒后面总要跟你算账的,所以还乖乖写吧。

单测目的

这个很重要,不清楚目的,就不知道接下来该怎么做。这样也行,那样好像也可以,埋头写一大片,很快就没了兴趣,而且还会很烦写单测。常见 web 服务大都是 controller、service、repository 三层架构。每一层都有各自的任务,基于此单测的目的也就不同。

  • repository 层主要负责数据的持久化,它里面除了读和写,其他的逻辑越少越好。所以该层单测要连上数据库,将数据真正的写入并且读出,从而确认该数据持久化,以及各种查找功能正确。推荐持久化层单测内嵌 H2 数据库,它是基于内存的,不受物理机限制,而且各个单测 case 之间互无影响。
  • service 层负责处理业务逻辑,很复杂的项目往往也就是复杂在这一层了,这层肯定会依赖该项目的 repository 层,还可能会依赖 Redis、kafka、mq、httpClinet 等第三方的东西。每次单测都调用第三方,哪有那么听话的第三方,每次都能正确返回结果。这层单测怎么写?把 service 层依赖的 repository 和 第三方的东西都 mock 出来,并 when 他们对应的方法,按照测试需求控制其方法返回值,从而控制 service 层的方法中代码的走向,确认自己写的逻辑是没问题的。在这一层中,一个方法往往需要好几个测试 case,一个正例,若干的异常情况。
  • controller 层负责和前端交互,接收请求、校验参数、返回处理结果。最理想的单测肯定是模拟出生产环境下前端请求的执行。这是可以办到的。

三层单测有各自的目的,而且不依赖于其余两层,如果真依赖了,那就使用 mock,控制返回值。从而实现“隔离”,本层单测只需要实现本层的目的,其余的都是不需要知道的。

controller 层单测

mockMVC 可以实现在单测中模拟生产环境中前端请求的执行逻辑。

Controller:

@RestController
@RequestMapping("/trains")
public class TrainController {

    private TrainService trainService;

    @Autowired
    public TrainController(TrainService trainService) {
        this.trainService = trainService;
    }

    @PostMapping("")
    public void create(@RequestBody @Validated ModelTrainsAddDTO req) {
        TrainEntity entity = new TrainEntity();
        BeanUtils.copyProperties(req, entity);
        trainService.create(entity);
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ModelTrainsAddDTO {
    @NotBlank
    private String name;
    private String modelType;
}

单元测试

@WebMvcTest(TrainController.class)
class TrainControllerTest {
    @MockBean
    private TrainService trainService;

    @Autowired
    private MockMvc mockMvc;

    @BeforeEach
    void setUp() {
        RestAssuredMockMvc.mockMvc(mockMvc);
    }

    @Test
    void should_200_if_create_train_succeed() {
        ModelTrainsAddDTO param = new ModelTrainsAddDTO("name", "model-type", "trian-fileds", "evail-files", "param");
        given()
                .contentType(ContentType.JSON)
                .body(param)
                .when()
                .post("/trains")
                .prettyPeek()
                .then()
                .statusCode(200);

        verify(trainService).create(any());
    }

    // 注解校验没通过,直接抛异常。
    @Test
    void should_500_if_name_is_null_when_create_train() {
        ModelTrainsAddDTO param = new ModelTrainsAddDTO(null, "model-type", "trian-fileds", "evail-files", "param");

        given()
                .contentType(ContentType.JSON)
                .body(param)
                .when()
                .post("/trains")
                .prettyPeek()
                .then()
                .statusCode(500);
    }
}

RestAssuredMockMvc

RestAssuredMockMvc 详细使用方法在这里,需要科学上网。它采用熟悉的 given-when-then 的场景格式定义测试写法,里面有段这样的描述:

given() — specifies the HTTP request details
when() — specifies the HTTP verb as well as the route
then() — validates the HTTP response

当然也还有别的写法,比如:mockMvc.perform()

依赖

<dependency>
	<groupId>io.rest-assured</groupId>
  <artifactId>spring-mock-mvc</artifactId>
  <version>4.5.1</version>
  <scope>test</scope>
</dependency>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java中,Controller层是负责处理客户端请求并返回响应的层级。要进行Controller层的单元测试,可以使用一些流行的测试框架,例如JUnit或Mockito。 下面是一个简单的示例,演示如何进行Controller层的单元测试: ```java 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.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; @WebMvcTest(YourController.class) // 替换为你的Controller类名 public class YourControllerTest { @Autowired private MockMvc mockMvc; @MockBean private YourService yourService; // 替换为你的Controller所依赖的Service类 @Test public void testYourController() throws Exception { // 构造模拟请求,并设置请求参数和请求类型 mockMvc.perform(MockMvcRequestBuilders.get("/your-endpoint") .param("param1", "value1") .param("param2", "value2") .accept(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().json("expected-response-json")); // 验证期望的响应 } } ``` 在上述示例中,我们使用了`@WebMvcTest`注解来指定要测试的Controller类。使用`@MockBean`注解来模拟所依赖的Service类,以便进行单元测试。然后,使用`mockMvc.perform`方法构造模拟请求,并使用`andExpect`方法来验证响应的状态码和内容。 请注意,这只是一个简单的示例,实际的单元测试可能需要更多的配置和验证步骤,具体取决于你的应用程序需求和测试目标。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值