本文介绍: Mock一个自动化测试永远绕不过去的话题。本文主要介绍使用标准库完成HTTP请求的Mock测试方法。可能有的小伙伴不太了解mock在实际自动化测试过程中的意义,在我的另外一篇博客中有比较详细的描述,在本文我们可以简单理解为它可以解决测试依赖。下面我们一起来学习它。将下面的内容保存到weather.goimport (“fmt“”net/httpconst (if err!= nil {根据我们前面场景设定,依赖接口是不可用的,所以。

目录

前言

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 main

import (
	"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    string
    URL    *url.URL
    Proto        string
    ProtoMajor    int
    ProtoMinor    int
    Header    Header
    Body    io.ReadCloser
    GetBody    func() (io.ReadCloser, error)
    ContentLength    int64
    TransferEncoding    []string
    Close    bool
...
type ResponseWriter interface {
    Header() Header
    Write([]byte) (int, error)
    WriteHeader(int)
}

从上面的定义可以看到两个结构体具体的参数方法定义。下面我们一起来学习net/http/httptest

httptest

假设现在有这么一个场景,我们现在有一个功能需要调用免费天气API来获取天气信息,但是这几天该API升级改造暂时不提供联调服务,而Boss希望该服务恢复后我们的新功能能直接上线,我们要怎么在服务不可用的时候完成相关的测试呢?答案就是使用Mock。

net/http/httptest就是原生里面提供Mock服务的包,使用它不用真正的启动一个http server(亦或者请求任意server),而且创建方法非常简单。下面我们一起来看看怎么使用它吧。

定义被测接口

将下面的内容保存weather.go中:

package weather

import (
	"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 weather

import (
	"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.URL
	fmt.Printf("Url:%sn", 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.URL
	fmt.Printf("Url:%sn", api)
	resp, err := GetWeatherInfo(api)
	if err != nil {
		fmt.Println("ERR:", err)
	} else {
		fmt.Println("resp:", resp)
	}
}

简单解释一下上面的部分代码

当然,我们也可以设置其他参数的值,也就是我们在最前面介绍的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.1
	StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
	StatusProcessing         = 102 // RFC 2518, 10.1

	StatusOK                   = 200 // RFC 7231, 6.3.1
	StatusCreated              = 201 // RFC 7231, 6.3.2
	StatusAccepted             = 202 // RFC 7231, 6.3.3
	StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4
	StatusNoContent            = 204 // RFC 7231, 6.3.5
	StatusResetContent         = 205 // RFC 7231, 6.3.6
    ...

综上,我们可以通过发送httptest来模拟出httpserver返回值来进行自己代码的测试,上面写的两条用例只是抛砖引玉,大家可以根据实际业务使用更多的场景来进行Mock。

总结

  • httptest
  • HandleFunc
  • 结构http.Request/http.ResponseWriter
  • http 响应码

 资料获取方法

留言777】

各位想获取源码教程资料的朋友请点赞 + 评论 + 收藏,三连!

三连之后我会在评论区挨个私信发给你们~

原文地址:https://blog.csdn.net/GDYY3721/article/details/132020782

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_21122.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注