【Golang 接口自动化08】使用标准库httptest完成HTTP请求的Mock测试
目录
前言
http包的HandleFunc函数
http.Request/http.ResponseWriter
httptest
定义被测接口
测试代码
测试执行
总结
资料获取方法
前言
Mock是一个做自动化测试永远绕不过去的话题。本文主要介绍使用标准库net/http/httptest完成HTTP请求的Mock的测试方法。
可能有的小伙伴不太了解mock在实际自动化测试过程中的意义,在我的另外一篇博客中有比较详细的描述,在本文中我们可以简单理解为它可以解决测试依赖。下面我们一起来学习它。
http包的HandleFunc函数
我们在前面的文章中介绍过怎么发送各种http请求,但是没有介绍过怎么使用golang启动一个http的服务。我们首先来看看怎么使用golang建立一个服务。
使用golang启动一个http服务非常简单,把下面的代码保存在httpServerDemo.go中,执行命令go run httpServerDemo.go就完成建立了一个监听在http://127.0.0.1:9090/上的服务。
package mainimport ("fmt""log""net/http"
)func httpServerDemo(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, `{"name":"Bingo","age":"18"}`)
}func main() {http.HandleFunc("/", httpServerDemo)err := http.ListenAndServe(":9090", nil)if err != nil {log.Fatal("ListenAndServe: ", err)}
}
访问http://127.0.0.1:9090/可以看到下面的内容。

介绍如何建立一个服务,是因为我们要学习建立服务需要使用到的两个结构体http.Request/http.ResponseWriter。下面我们一起来看看他们的具体内容。
http.Request/http.ResponseWriter
type Request struct {Method stringURL *url.URLProto stringProtoMajor intProtoMinor intHeader HeaderBody io.ReadCloserGetBody func() (io.ReadCloser, error)ContentLength int64TransferEncoding []stringClose bool
...
type ResponseWriter interface {Header() HeaderWrite([]byte) (int, error)WriteHeader(int)
}
从上面的定义可以看到两个结构体具体的参数和方法定义。下面我们一起来学习net/http/httptest。
httptest
假设现在有这么一个场景,我们现在有一个功能需要调用免费天气API来获取天气信息,但是这几天该API升级改造暂时不提供联调服务,而Boss希望该服务恢复后我们的新功能能直接上线,我们要怎么在服务不可用的时候完成相关的测试呢?答案就是使用Mock。
net/http/httptest就是原生库里面提供Mock服务的包,使用它不用真正的启动一个http server(亦或者请求任意的server),而且创建方法非常简单。下面我们一起来看看怎么使用它吧。
定义被测接口
将下面的内容保存到weather.go中:
package weatherimport ("encoding/json""fmt""io/ioutil""net/http"
)const (ADDRESS = "shenzhen"
)type Weather struct {City string `json:"city"`Date string `json:"date"`TemP string `json:"temP"`Weather string `json:"weather"`
}func GetWeatherInfo(api string) ([]Weather, error) {url := fmt.Sprintf("%s/weather?city=%s", api, ADDRESS)resp, err := http.Get(url)if err != nil {return []Weather{}, err}if resp.StatusCode != http.StatusOK {return []Weather{}, fmt.Errorf("Resp is didn't 200 OK:%s", resp.Status)}bodybytes, _ := ioutil.ReadAll(resp.Body)personList := make([]Weather, 0)err = json.Unmarshal(bodybytes, &personList)if err != nil {fmt.Errorf("Decode data fail")return []Weather{}, fmt.Errorf("Decode data fail")}return personList, nil
}
根据我们前面的场景设定,GetWeatherInfo依赖接口是不可用的,所以resp, err := http.Get(url)这一行的err肯定不为nil。为了不影响天气服务恢复后我们的功能能直接上线,我们在不动源码,从单元测试用例入手来完成测试。
测试代码
将下面的内容保存到weather_test.go中::
package weatherimport ("encoding/json""fmt""net/http""net/http/httptest""testing"
)var weatherResp = []Weather{{City: "shenzhen",Date: "10-22",TemP: "15℃~21℃",Weather: "rain",},{City: "guangzhou",Date: "10-22",TemP: "15℃~21℃",Weather: "sunny",},{City: "beijing",Date: "10-22",TemP: "1℃~11℃",Weather: "snow",},
}
var weatherRespBytes, _ = json.Marshal(weatherResp)func TestGetInfoUnauthorized(t *testing.T) {ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {w.WriteHeader(http.StatusUnauthorized)w.Write(weatherRespBytes)if r.Method != "GET" {t.Errorf("Except 'Get' got '%s'", r.Method)}if r.URL.EscapedPath() != "/weather" {t.Errorf("Except to path '/person',got '%s'", r.URL.EscapedPath())}r.ParseForm()topic := r.Form.Get("city")if topic != "shenzhen" {t.Errorf("Except rquest to have 'city=shenzhen',got '%s'", topic)}}))defer ts.Close()api := ts.URLfmt.Printf("Url:%s\n", api)resp, err := GetWeatherInfo(api)if err != nil {t.Errorf("ERR:", err)} else {fmt.Println("resp:", resp)}
}func TestGetInfoOK(t *testing.T) {ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {w.WriteHeader(http.StatusOK)w.Write(weatherRespBytes)if r.Method != "GET" {t.Errorf("Except 'Get' got '%s'", r.Method)}if r.URL.EscapedPath() != "/weather" {t.Errorf("Except to path '/person',got '%s'", r.URL.EscapedPath())}r.ParseForm()topic := r.Form.Get("city")if topic != "shenzhen" {t.Errorf("Except rquest to have 'city=shenzhen',got '%s'", topic)}}))defer ts.Close()api := ts.URLfmt.Printf("Url:%s\n", api)resp, err := GetWeatherInfo(api)if err != nil {fmt.Println("ERR:", err)} else {fmt.Println("resp:", resp)}
}
简单解释一下上面的部分代码:
- 我们通过httptest.NewServer创建了一个测试的http server
- 通过变量r *http.Request读请求设置,通过w http.ResponseWriter设置返回值
- 通过ts.URL来获取请求的URL(一般都是http://ip:port)也就是实际的请求url
- 通过r.Method来获取请求的方法,来测试判断我们的请求方法是否正确
- 获取请求路径:r.URL.EscapedPath(),本例中的请求路径就是"/weather"
- 获取请求参数:r.ParseForm,r.Form.Get("city")
- 设置返回的状态码:w.WriteHeader(http.StatusOK)
- 设置返回的内容(也就是我们想要的结果):w.Write(personResponseBytes),注意w.Write()接收的参数是[]byte,所以通过json.Marshal(personResponse)转换。
当然,我们也可以设置其他参数的值,也就是我们在最前面介绍的http.Request/http.ResponseWriter这两个结构体的内容。
测试执行
在终端中进入我们保存上面两个文件的目录,执行go test -v就可以看到下面的测试结果:
bingo@Mac httptest$ go test -v
=== RUN TestGetInfoUnauthorized
Url:http://127.0.0.1:55816
--- FAIL: TestGetInfoUnauthorized (0.00s)person_test.go:55: ERR:%!(EXTRA *errors.errorString=Resp is didn't 200 OK:401 Unauthorized)
=== RUN TestGetInfoOK
Url:http://127.0.0.1:55818
resp: [{shenzhen 10-22 15℃~21℃ rain} {guangzhou 10-22 15℃~21℃ sunny} {beijing 10-22 1℃~11℃ snow}]
--- PASS: TestGetInfoOK (0.00s)
FAIL
exit status 1
FAIL bingo.com/blogs/httptest 0.016s
可以看到两条测试用例成功了一条失败了一条,失败的原因就是我们设置的接口响应码为401(w.WriteHeader(http.StatusUnauthorized)),这个可能会在调用其他服务时遇到,所以有必要进行测试。更多的响应码我们可以在我们的golang安装目录下找到,比如博主的路径是:
/usr/local/go/src/net/http/status.go
这个文件中定义了几乎所有的http响应码:
StatusContinue = 100 // RFC 7231, 6.2.1StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2StatusProcessing = 102 // RFC 2518, 10.1StatusOK = 200 // RFC 7231, 6.3.1StatusCreated = 201 // RFC 7231, 6.3.2StatusAccepted = 202 // RFC 7231, 6.3.3StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4StatusNoContent = 204 // RFC 7231, 6.3.5StatusResetContent = 205 // RFC 7231, 6.3.6...
综上,我们可以通过不发送httptest来模拟出httpserver和返回值来进行自己代码的测试,上面写的两条用例只是抛砖引玉,大家可以根据实际业务使用更多的场景来进行Mock。
总结
- httptest
- HandleFunc
- 结构体
http.Request/http.ResponseWriter - http 响应码
资料获取方法
【留言777】


各位想获取源码等教程资料的朋友请点赞 + 评论 + 收藏,三连!
三连之后我会在评论区挨个私信发给你们~
相关文章:
【Golang 接口自动化08】使用标准库httptest完成HTTP请求的Mock测试
目录 前言 http包的HandleFunc函数 http.Request/http.ResponseWriter httptest 定义被测接口 测试代码 测试执行 总结 资料获取方法 前言 Mock是一个做自动化测试永远绕不过去的话题。本文主要介绍使用标准库net/http/httptest完成HTTP请求的Mock的测试方法。 可能有…...
SpringBoot自定义注解 + AOP+分布式Redis 防止重复提交
第一步 引入依赖pom.xml: <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.16.3</version> <!-- 使用最新版本 --></dependency><dependency><groupId&g…...
3.yum安装分布式LNMP--剧本
文章目录 修改hosts创建剧本文件 修改hosts vim /etc/ansible/hosts[webservers] 192.168.242.67[dbservers] 192.168.242.68[phpservers] 192.168.242.69创建剧本文件 vim lnmp.yaml- name: nginx playhosts: webserversremote_user: rootvars:- http_port: 192.168.242.67:…...
论文笔记:Fine-Grained Urban Flow Prediction
2021 WWW 1 intro 细粒度城市流量预测 两个挑战 细粒度数据中观察到的网格间的转移动态使得预测变得更加复杂 需要在全局范围内捕获网格单元之间的空间依赖性单独学习外部因素(例如天气、POI、路段信息等)对大量网格单元的影响非常具有挑战性——>论…...
系统集成|第八章(笔记)
目录 第八章 进度管理8.1 主要过程8.1.1 规划进度管理8.1.2 定义活动8.1.3 排列活动顺序8.1.4 估算活动资源8.1.5 估算活动持续时间8.1.6 制定进度计划8.1.7 控制进度 8.2 注意与问题 上篇:第七章、范围管理 第八章 进度管理 8.1 主要过程 包括: 规划进…...
【分布式】分布式唯一 ID 的 几种生成方案以及优缺点snowflake优化方案
在互联网的业务系统中,涉及到各种各样的ID,如在支付系统中就会有支付ID、退款ID等。那一般生成ID都有哪些解决方案呢?特别是在复杂的分布式系统业务场景中,我们应该采用哪种适合自己的解决方案是十分重要的。下面我们一一来列举一…...
FFmpeg5.0源码阅读——av_interleaved_write_frame
摘要:本文主要详细描述FFmpeg中封装时写packet到媒体文件的函数av_interleaved_write_frame的实现。 关键字:av_interleaved_write_frame 读者须知:读者需要熟悉ffmpeg的基本使用。 1 基本调用流程 av_interleaved_write_frame的基本…...
力扣 70. 爬楼梯
题目来源:https://leetcode.cn/problems/climbing-stairs/description/ C题解(来源代码随想录): 本质上是一道斐波那契数题。 动规五部曲:定义一个一维数组来记录不同楼层的状态 确定dp数组以及下标的含义。dp[i]&am…...
AVFoundation - 媒体捕捉
文章目录 注意使用 NSCameraUsageDescriptioniOS 的摄像头可能比 Mac 更多功能特性@interface Capture ()<AVCaptureFileOutputRecordingDelegate>@property (strong, nonatomic) AVCaptureSession *captureSession; @property (weak, nonatomic) AVCaptureDeviceInput *…...
【新版系统架构补充】-嵌入式技术
嵌入式微处理体系结构 冯诺依曼结构 传统计算机采用冯诺依曼结构,也称普林斯顿结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构 冯诺依曼的计算机程序和数据共用一个存储空间,程序指令存储地址和数据存储地址指向同一个存…...
fpga开发--蜂鸣器发出连续不同的音调
描述 使用fpga蜂鸣器连续发出do,re,mi,fa,so,la,xi七个不同的音调,每个音调的持续时间为0.5s。 思路 采用状态机实现音调的转化,当do状态持续了0.5s之后转移到re状态,…...
Redis 主从同步原理
一、什么是主从同步? 主从同步,就是将数据冗余备份,主库(Master)将自己库中的数据,同步给从库(Slave)。 从库可以一个,也可以多个,如图所示: 二…...
opencv-28 自适应阈值处理-cv2.adaptiveThreshold()
什么是自适应阈值处理? 对于色彩均衡的图像,直接使用一个阈值就能完成对图像的阈值化处理。但是,有时图像的色彩是不均衡的,此时如果只使用一个阈值,就无法得到清晰有效的阈值分割结果图像。 有一种改进的阈值处理技术ÿ…...
Java泛型5——泛型通配符
注:以下内容基于Java 8,所有代码都已在Java 8环境下测试通过 目录: Java泛型1——概述Java泛型2——泛型类Java泛型3——泛型接口Java泛型4——泛型方法Java泛型5——泛型通配符Java泛型6——类型擦除 什么是通配符 在Java中,类…...
牛客 AB25 ranko的手表 JAVA 枚举
描述 ranko 的手表坏了,正常应该显示 xx:xx 的形式(4 个数字),比如下午 1 点半应该显示 13:30 ,但现在经常会有一些数字有概率无法显示。 ranko 在 �1t1 时刻看了下时间,过了一段时间在 &am…...
常微分方程建模R包ecode(二)——绘制相速矢量场
本节中我们考虑一个更为复杂的常微分方程模型, d X C d t ν ( X A Y A ) − β ⋅ X C ⋅ ( Y C Y A ) − ( μ g ) ⋅ X C , ( 1 ) d Y C d t β ⋅ X C ⋅ ( Y C Y A ) − ( μ g ρ ) ⋅ Y C , ( 2 ) d X A d t g ⋅ X C − β ⋅ X A ⋅ ( Y C Y A …...
学习C#编写上位机的基础知识和入门步骤:
00001. 掌握C#编程语言基础和.NET框架的使用。 00002. 学习WinForm窗体应用程序开发技术,包括控件的使用和事件驱动编程。 00003. 熟悉基本的数据结构和算法知识,如链表、栈、队列等。 00004. 理解串口通信协议和通信方法,用于与底层硬件设…...
简单高效!低代码搭建销售自动化程序的方法与实践
在当今数字化时代,销售自动化成为了提高销售效率和业绩的重要手段之一。而低代码平台的兴起,使得搭建销售自动化程序变得更加简单和高效。本文将介绍低代码平台及其优势,并探讨如何利用低代码平台搭建销售自动化程序。 1、低代码平台 1&…...
第九十三回 在Flutter中mock数据
文章目录 概念介绍使用方法示例代码 我们在上一章回中介绍了"在Flutter中解析JSON数据"相关的内容,本章回中将介绍 如何mock数据.闲话休提,让我们一起Talk Flutter吧。 概念介绍 我们在本章回中介绍的mock数据主要是通过相关的代码模拟服务器…...
进程与线程的区别与联系
多进程已经可以很好的实现并发编程的效果了,但是仍然有一个明显的缺点:进程太重了,进程消耗的资源更多,速度更慢。如果进程创建销毁不频繁,那么还好,一旦需要大规模创建和销毁进程,开销就比较大…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
