单元测试利器 - Mock

 

为什么会有这篇blog呢,起因上周末看了一个朋友的代码。

结果随便打开他们的测试用例,看一下,这是嘛?

周末补了一下这块的空缺,学无止尽嘛:大致看了一下主体还是很简单的,

 

官网地址:https://site.mockito.org/

GitHub地址:https://github.com/hehonghui/mockito-doc-zh#0

文档地址:https://javadoc.io/static/org.mockito/mockito-core/3.3.3/org/mockito/Mockito.html

 

 

目录

springboot快速搭建一个web项目,用作编写测试用例代码

测试用例 - mockmvc

测试用例 - Mockito


springboot快速搭建一个web项目,用作编写测试用例代码

 

pom.xml(基于boot2.x,已经内置junit5跟mock了)

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

 

花几分钟写一个添加用户的功能,DB操作就直接写伪sql了,为啥?因为我懒...

 

controller

package com.mock.demo.controller;

import com.mock.demo.entry.User;
import com.mock.demo.service.SmsService;
import com.mock.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
import static com.mock.demo.service.impl.UserServiceImpl.userMap;

/**
 * @author 954L
 * @create 2020/3/24 20:44
 */
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private SmsService smsService;

    @PostMapping("user")
    public Map<String, Object> addAndSendSms(@RequestBody User user,
                                             @RequestParam(value = "sendSmsType", defaultValue = "0")
                                                     Integer sendSmsType) {
        Map<String, Object> result = new HashMap<>();
        String name = user.getName();
        // 发送注册短信
        boolean status = sendSmsType.equals(1) ? smsService.sendSms() : true;
        // 查重 & 添加
        if (status) status = userMap.get(name) != null ?
                false : userService.add(user);
        result.put("status", status);
        return result;
    }

    @DeleteMapping("user/{name}")
    public Map<String, Object> del(@PathVariable("name") String name){
        Map<String, Object> result = new HashMap<>();
        // 先添加,再删除
        userService.add(User.builder().name(name).build());
        boolean status = userService.del(name) ;
        result.put("status", status);
        return result;
    }

}

 

service

package com.mock.demo.service;

import com.mock.demo.entry.User;

/**
 * @author 954L
 * @create 2020/3/24 20:47
 */
public interface UserService {

    boolean add(User user);

    boolean del(String name);

}
package com.mock.demo.service;

/**
 * @author 954L
 * @create 2020/3/24 20:39
 */
public interface SmsService {

    boolean sendSms();

}

 

impl

package com.mock.demo.service.impl;

import com.mock.demo.entry.User;
import com.mock.demo.service.UserService;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 954L
 * @create 2020/3/24 20:47
 */
@Service
public class UserServiceImpl implements UserService {

    public static Map<String, User> userMap = new ConcurrentHashMap();

    @Override
    public boolean add(User user) {
        // 添加用户
        userMap.put(user.getName(), user);
        return userMap.containsKey(user.getName());
    }

    @Override
    public boolean del(String name) {
        userMap.remove(name);
        return !userMap.containsKey(name);
    }

}
package com.mock.demo.service.impl;

import com.mock.demo.service.SmsService;
import org.springframework.stereotype.Service;

/**
 * @author 954L
 * @create 2020/3/24 20:39
 */
@Service
public class SmsServiceImpl implements SmsService {

    /**
     * 提供给mockito测试模拟对象,返回true
     * @return
     */
    public boolean sendSms(){
        // 无短信功能
        return false;
    }

}

 

entity(代码包名写错了,将就下)

package com.mock.demo.entry;

import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;

/**
 * @author 954L
 * @create 2020/3/24 20:46
 */
@Data
@Builder
public class User {

    private String name;
    private Integer age;

    @Tolerate
    public User(){}

}

 

测试用例 - mockmvc

mockmvc

模拟web程序发送一个请求

方法 说明
mockMvc.perform 执行一个请求
MockMvcRequestBuilders.get("XXX") 构造一个请求
ResultActions.param 添加请求传值
ResultActions.accept()) 执行一个请求 如MediaType.TEXT_HTML_VALUE
ResultActions.andExpect 添加执行完成后的断言。 等同于 Assert.assertEquals
ResultActions.andDo 添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息
ResultActions.andReturn 表示执行完成后返回相应的结果。

controllerMockMvcTest

package com.mock.demo.controller;

import com.mock.demo.MockApplication;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

/**
 * @author 954L
 * @create 2020/3/24 20:03
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MockApplication.class)
@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc
class UserControllerMockMvcTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void add() throws Exception {
        JSONObject paramJson = new JSONObject();
        paramJson.put("name", "Tony");
        paramJson.put("age", "18");
        mockMvc.perform(MockMvcRequestBuilders.post("/user")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .content(paramJson.toString())
                .accept(MediaType.ALL))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print());
    }

    @Test
    void del() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.delete("/user/Tony")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .accept(MediaType.ALL))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print());
    }

}

 

测试用例 - Mockito

mockito

模拟外部依赖,将模拟产生的外部依赖数据与测试代码一起执行

方法 说明
Mockito.mock(classToMock) 模拟对象
Mockito.verify(mock) 验证行为是否发生
Mockito.when(methodCall).thenReturn(value1).thenReturn(value2) 触发时第一次返回value1,第n次都返回value2
Mockito.doThrow(toBeThrown).when(mock).[method] 模拟抛出异常。
Mockito.mock(classToMock,defaultAnswer) 使用默认Answer模拟对象
Mockito.when(methodCall).thenReturn(value) 参数匹配
Mockito.doReturn(toBeReturned).when(mock).[method]

参数匹配(直接执行不判断)

Mockito.when(methodCall).thenAnswer(answer)) 预期回调接口生成期望值
Mockito.doAnswer(answer).when(methodCall).[method] 预期回调接口生成期望值(直接执行不判断)
Mockito.spy(Object) 用spy监控真实对象,设置真实对象行为
Mockito.doNothing().when(mock).[method] 不做任何返回

Mockito.doCallRealMethod().when(mock).[method] //等价于Mockito.when(mock.[method]).thenCallRealMethod();

调用真实的方法
reset(mock) 重置mock

 

controllerMockItoTest

package com.mock.demo.controller;

import com.mock.demo.MockApplication;
import com.mock.demo.entry.User;
import com.mock.demo.service.SmsService;
import com.mock.demo.service.UserService;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit4.SpringRunner;

import static com.mock.demo.service.impl.UserServiceImpl.userMap;

/**
 * @author 954L
 * @create 2020/3/24 20:03
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MockApplication.class)
@ExtendWith(SpringExtension.class)
class UserControllerMockItoTest {

    @Autowired
    private UserService userService;

    @Test
    void add() {
        // 测试添加用户service层,因为依赖短信功能,故这里模拟短信service功能
        SmsService smsService = Mockito.mock(SmsService.class);
        // 当执行smsService.sendSms(),模拟返回true
        Mockito.when(smsService.sendSms()).thenReturn(true);

        User user = User.builder().name("testName").age(20).build();
        String name = user.getName();
        // 发送注册短信
        boolean status = smsService.sendSms();
        // 查重 & 添加
        if (status) status = userMap.get(name) != null ?
                false : userService.add(user);
        Assert.assertTrue(status);
    }

}

 

 

对你有帮助的话,右上角给个赞呗~

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章