在 Spring 中 Mock RestTemplate

2023-10-27

如果我们程序中使用了 RestTemplate 进行 HTTP API 调用。通常在编写单元测试时,为了让测试可控,会将 RestTemlate 调用进行 mock,而不是进行真实的 HTTP API 调用。

这里,我们将介绍两种 mock RestTemplate 调用的方法。一个是比较流行的 Mockito 模拟库,另一个是使用 Spring Test 提供的 MockRestServiceServer 模拟服务器,它可以创建模拟服务器以定义服务器交互。

使用 Mockito 模拟

使用 Mockito 模拟 RestTemplate 测试我们的服务将像其他任何涉及模拟的测试一样简单,下面我们就来看看怎么使用吧。

下面这段代码描述的是,一个 UserService 类,该类通过 HTTP 请求获取用户信息。

@Service("userServiceRest")
public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    public List<UserVO> getUsers() {
        UserVO[] users = restTemplate.getForObject("http://localhost:8080/users", UserVO[].class);
        if (users == null) {
            return Collections.emptyList();
        }
        return Arrays.asList(users);
    }
}

现在,我们要为 UserService 类的 getUsers() 编写单元测试。测试代码如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
    
    // 模拟一个假的 RestTemplate 实例
    @Mock
    private RestTemplate restTemplate;

    @Autowired
    @Qualifier("userServiceRest")
    @InjectMocks
    private UserService userService;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testGetUsers() {
        UserVO mockUser = new UserVO(1, "mock-test");
        // 模拟一个假的请求
        Mockito.when(restTemplate.getForObject("http://localhost:8080/users", UserVO[].class))
                .thenReturn(new UserVO[]{mockUser});
        List<UserVO> users = userService.getUsers();
        Assert.assertFalse(CollectionUtils.isEmpty(users));
        Assert.assertEquals(1, users.size());
        UserVO userVO = users.get(0);
        Assert.assertEquals(mockUser.getId(), userVO.getId());
        Assert.assertEquals(mockUser.getName(), userVO.getName());
    }
}

上面的单元测试中,我们首先使用 Mockito 模拟库中 @Mock 注解创建一个假 RestTemplate 实例。

然后,我们使用 @InjectMocks 注释了 UserService 实例,以将模拟的实例注入到其中。

最后,在测试方法中,我们使用 Mockito 的 when() 和 then() 方法定义了模拟的行为。

使用 Spring Test 模拟

Spring Test 模块中包含一个叫 MockRestServiceServer 的模拟服务器。通过这种方法,我们将服务器配置为在通过 RestTemplate 实例调度特定请求时返回特定对象。最后,我们可以在该服务器实例上调用 verify() 方法验证是否满足所有期望。

MockRestServiceServer 实际上是通过使用 MockClientHttpRequestFactory 拦截 HTTP API 调用来工作的。根据我们的配置,它会创建预期请求和相应的响应列表。当 RestTemplate 实例调用 API 时,它将在期望列表中查找请求并返回相应的响应。因此,它无需在任何其他端口上运行 HTTP 服务器来发送模拟响应。

下面,我们使用 MockRestServiceServer 为 UserService 类的 getUsers() 编写单元测试,代码如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceMockRestServiceServerTest {

    @Autowired
    private UserService userService;

    @Autowired
    private RestTemplate restTemplate;

    private MockRestServiceServer mockServer;

    private ObjectMapper mapper = new ObjectMapper();

    @Before
    public void init() {
        mockServer = MockRestServiceServer.createServer(restTemplate);
    }

    @Test
    public void testGetUsers() throws Exception {
        UserVO mockUser = new UserVO(1, "mock-test");
        // 模拟 RestTemplate 请求
        mockServer.expect(ExpectedCount.once(),
                MockRestRequestMatchers.requestTo(new URI("http://localhost:8080/users")))
                .andExpect(MockRestRequestMatchers.method(HttpMethod.GET))
                .andRespond(MockRestResponseCreators.withStatus(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(mapper.writeValueAsString(new UserVO[]{mockUser}))
                );
        List<UserVO> users = userService.getUsers();
        Assert.assertFalse(CollectionUtils.isEmpty(users));
        UserVO userVO = users.get(0);
        Assert.assertEquals(mockUser.getId(), userVO.getId());
        Assert.assertEquals(mockUser.getName(), userVO.getName());
        mockServer.verify();
    }

}

上面代码段中,我们使用 MockRestRequestMatchers 和 MockRestResponseCreators 中的静态方法以清晰易读的方式定义 REST 调用的期望和响应:

MockRestRequestMatchers.requestTo(new URI("http://localhost:8080/users"));

MockRestRequestMatchers.method(HttpMethod.GET);

MockRestResponseCreators.withStatus(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(mapper.writeValueAsString(new UserVO[]{mockUser});

这里需要提醒大家的是,测试类中的 RestTemplate 应该与 UserService 类中使用的实例相同。为了确保这一点,我们需要在 spring 容器中定义 RestTemplate bean,并在测试和实现中自动连接实例。

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

当我们编写集成测试并且只需要模拟外部 HTTP 调用时,使用 MockRestServiceServer 非常有用。

最后,最后,小伙伴们可以在 GitHub 中获取源码

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Spring 中 Mock RestTemplate 的相关文章

随机推荐

  • 使用json-server出现的 500 internal server error

    首先声明 是使用json server搭建本地数据接口 在请求数据中出现的 500错误哦 最近在做一个项目 为了找工作使用的 o 使用的是json server搭建的本地数据接口 在实现注册页面向搭建的数据接口post数据的时候 遇到了一个
  • Tensorflow深度学习笔记(四)-利用神经网络预测非线性回归示例

    本文主要分享一个利用神经网络来预测非线性回归的示例 首先 定义生成我们的测试数据 即y data np square x data noise 通过x data的平方再加上噪声来生成y data 然后 利用神经网络 将x data作为输入
  • 区块链在数字经济中的地位和作用都有哪些?

    区块链是校正经济数字化进程的关键性底层技术架构 具有不可替代的作用 数字经济的关键技术包括大数据 物联网 人工智能 区块链等 技术是实现经济数字化的手段 不同的技术在经济数字化过程中扮演着不同的角色 云计算 边缘计算 IPFS按需提供数据存
  • Android加密和解密(DES,AES,MD5)

    加密是通过加密算法和加密密钥将明文转变为密文的过程 解密是其逆过程 加密算法有很多种 一般可以分为对称加密 如DES AES等 非对称加密 如RSA等 和单向加密 如MD5等 3类算法 1 在对称加密算法中 双方使用的密钥相同 要求解密方事
  • 科技查新报告的查新流程

    科技查新可以当天出报告吗 亲 我着急做一篇科技查新 当天能出报告吗 一天的时间是不够的 额 一般是多久呢 二到十个工作日 大家可能会很疑惑 为啥连查新员自己都不好确定报告的完成时间呢 这就要说说一份科技查新报告是怎么写出来的了 看完这个业务
  • C语言编写学生籍贯信息记录簿

    学生籍贯信息记录簿 前言 这是本人第一次写博客 有不足之处请见谅 本文是利用简单的C语言编写的学生籍贯信息记录簿 写这篇是为了记录学习过程 也为后来者提供一些参考和思路 我使用的平台为Visual C 6 0 其功能主要分为录入 保存 查询
  • hadoop报错:Exception in thread “main“ org.apache.hadoop.security.AccessControlException: Permission de

    Exception in thread main org apache hadoop security AccessControlException Permission denied user xuan access WRITE inod
  • R 安装函数包及绘制图形

    R语言绘制简单条形图 一 载入数据 1 安装函数包 2 导入文件 3 读取数据 csv文件 二 绘制图形 绘制简单条形图 一 载入数据 1 安装函数包 在控制台console输入命令 install packages bruceR 查看是否
  • Qt笔记(五十九)之Qt使用自定义字体

    一 前言 楼主在实际开发中 遇到有使用对界面字体要求的需求 有些客户希望用自己的字体 一开始 楼主还是有点懵 但是想到Qt一开始肯定是考虑到这个方式了 于是就去寻找 果然有 二 设置字体 1 使用内置字体 常规的设置方式 QApplicat
  • python语法-函数多返回值、函数多种传参方式、匿名函数

    python语法 函数多返回值 函数多种传参方式 匿名函数 函数多返回值 函数多个返回值 def test return return 1 hello True x y z test return print f x x ny y nz z
  • angularJs中的发送请求例子

    http 发送请求 url http localhost 8080 teacher api login method post data obj success function data rootScope data data 返回结果
  • MacBookAir M1 遇到 nodesass不兼容问题的解决方案

    引言 在工作中接手了一个新项目 项目采用了nodesass 想尽方法让他跑起来 却抛出一个错误 Node Sass does not yet support your current environment OS X Unsupported
  • [1151]python连接 redis cluster集群

    使用pip search查看可安装的Redis模块版本 PS D code gt pip install pip search 安装pip search模块 PS D code gt pip search redis 利用pip searc
  • 从DEMO到完成项目过程的流程

    一 项目评审 认真看demo需求是关键 根据项目demo原型 将项目功能点细分 按每个功能点实现的大致期限 去估计整个项目的期限 一旦项目评估预期确定 需要严格按照预期来实现 决不能拖拉 二 项目实施中 项目实施中 尽量以实现demo功能为
  • QQ机器人相关指令实现-对接小夹子

    代码地址以及视频地址 代码地址 视频地址 实现小夹子网的对接 打开小夹子网了解如何对接 小夹子网 小夹子API对接文档 完成认证的功能 通过小夹子网编写相关常量信息 public interface ClipWebConstants 基础路
  • 判断链表有环并返回入环的第一个节点

    这个问题可以两部分组成 1 首席判读链表是否有环 2 有环的话 在公共点拆开 设在ptr1 ptr2 那么ptr2前进一步 ptr2 ptr2 gt next ptr1拆链表 ptr1 gt next NULL 此时 就有两个链表了 一个是
  • 9008小米售后权限账号_小米救砖教程

    今早在头条看到个微头条 大概意思是小米9有锁机只需要几百块 然后就有人在下边评论说 为什么要出这期解锁教程呢 是因为某些沙雕说小米9进9008刷机不需要售后权限 当然并没有在上图出现 我当场笑喷 首先普及一下 mi8及以上用9008刷机需要
  • Camera根据鼠标操作观察物体(移动,转动,滑动)

    领导安排了一个任务 客户需求根据鼠标操作可以更详细的观察物体 于是开始研究Camera的使用 客户要求 1 按住右键可以旋转观察物体 2 按住左键可以平移的上下拖动视角 3 滚动滑轮可以拉近距离 思路 需要移动的是摄像机而不是物体 因为物体
  • 猜数字游戏

    define CRT SECURE NO WARNINGS 1 include
  • 在 Spring 中 Mock RestTemplate

    如果我们程序中使用了 RestTemplate 进行 HTTP API 调用 通常在编写单元测试时 为了让测试可控 会将 RestTemlate 调用进行 mock 而不是进行真实的 HTTP API 调用 这里 我们将介绍两种 mock