1. 程式人生 > >Golang單元測試之httptest使用

Golang單元測試之httptest使用

現在有一個需求那就是,我們需要使用Golang的net/http包中的http.Get(url)方法去向伺服器端請求資料,但是負責服務端的同事並沒有將介面實現(可能是同事太忙,把妹,喝酒,扯淡, XO等等)以至於你只知道返回資料的json格式,然而無法請求到真實的資料,但是你的工作進度並不能因為同事而耽誤,需要測試你的程式碼的正確性,那麼怎麼辦?辦法就是通過單元測試中的httptest,實現http server,並設定好返回值,那麼http.Get(url)的請求就會直接打到單元測試的http server上,同時得到你設定好的返回值,你就可以繼續去處理資料,測試你的程式碼邏輯了。

一、匯入

設定一個場景:

需要向伺服器端去獲取住址為"shanghai"的所有人的資訊,請求函式為:

func GetInfo(api string) ([]Person, error) 
這個方法包括:

1.向服務端傳送get請求獲取資料

2.將資料序列化person的資訊

3.如果出現錯誤,返回error

需要測試的內容:

1.需要測試返回值是我們設定的值:

var personResponse = []Person{
			  {
				Name : "wahaha",
				Address : "shanghai",
				Age : 20,
			  },
	                  {
				Name : "lebaishi",
				Address : "shanghai",
				Age : 10,
			  },
 			}
2.需要測試GetInfo方法是傳送了正確的http請求,及method是否正確,路徑是否正確,請求引數是否正確。

3.返回狀態碼是否正確。

二、Golang testing基礎

Go提供了一個testing包來寫單元測試。假設我們有個檔案叫person.go,那麼我們的測試檔案就需要明明為person_test.go

package person

import (
	"testing"
)

func TestPublishUnreachable(t *testing.T) {
	api := "http://localhost:8090"
	_, err := GetInfo(api)
	if err != nil {
		t.Errorf("GetInfo() return an error")
	}
}
注:

>測試函式以Test*開頭。

>測試函式將*testing.T作為引數,可以在失敗的情況下使用Errorf()方法。

>在包內使用go test來執行單元測試。

這個單元測試將會失敗,因為我們還沒有實現GetInfo()方法,下面是person.go檔案:

package person

import (
	"net/http"
	"fmt"
)

const (
	ADDRESS = "shanghai"
)

type Person struct {
	Name string `json:"name"`
	Address string `json:"address"`
	Age int `json:"age"`
}

func GetInfo(api string) ([]Person, error) {
	url := fmt.Sprintf("%s/person?addr=%s", api, ADDRESS)
	resp, err := http.Get(url)

	if err != nil {
		return []Person{}, err
	}

	if resp.StatusCode != http.StatusOK {
		return []Person{}, fmt.Errorf("get info didn’t respond 200 OK: %s", resp.Status)
	}

	return nil, nil
}
當然執行go test也會返回一個錯誤,因為請求地址的問題,請求的並不是一個實際上的http server,那麼自然也不會有正常的返回。

三、Golang httptest

上面一個例子很有用,但是如何去傳送一個真正的http request而不去真正的啟動一個http server(亦或者請求任意的server)?答案是使用Go 的httptest包,這個包可以非常簡單的建立一個測試的http server,那麼下面我們將展示一下完整的程式碼,並解釋一下整體的測試流程:

person.go:

package person

import (
	"net/http"
	"fmt"
	"io/ioutil"
	"encoding/json"

	"github.com/astaxie/beego/logs"
)

const (
	ADDRESS = "shanghai"
)

type Person struct {
	Name string `json:"name"`
	Address string `json:"address"`
	Age int `json:"age"`
}

func GetInfo(api string) ([]Person, error) {
	url := fmt.Sprintf("%s/person?addr=%s", api, ADDRESS)
	resp, err := http.Get(url)

	defer resp.Body.Close()

	if err != nil {
		return []Person{}, err
	}

	if resp.StatusCode != http.StatusOK {
		return []Person{}, fmt.Errorf("get info didn’t respond 200 OK: %s", resp.Status)
	}

	bodyBytes, _ := ioutil.ReadAll(resp.Body)
	personList := make([]Person,0)
	err = json.Unmarshal(bodyBytes, &personList)
	if err != nil {
		logs.Error("decode data fail")
		return []Person{}, fmt.Errorf("decode data fail")
	}

	return personList, nil
}

person_test.go:

package person

import (
	"testing"
	"net/http/httptest"
	"net/http"
	"fmt"
	"encoding/json"
)

var personResponse = []Person{
			  {
				Name : "wahaha",
				Address : "shanghai",
				Age : 20,
			  },
	                  {
				Name : "lebaishi",
				Address : "shanghai",
				Age : 10,
			  },
 			}

var personResponseBytes, _ = json.Marshal(personResponse)

func TestPublishWrongResponseStatus(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write(personResponseBytes)
		if r.Method != "GET"{
			t.Errorf("Expected 'GET' request, got '%s'", r.Method)
		}
		if r.URL.EscapedPath() != "/person" {
			t.Errorf("Expected request to '/person', got '%s'", r.URL.EscapedPath())
		}
		r.ParseForm()
		topic := r.Form.Get("addr")
		if topic != "shanghai" {
			t.Errorf("Expected request to have 'addr=shanghai', got: '%s'", topic)
		}
	}))

	defer ts.Close()
	api := ts.URL
	fmt.Println("url:", api)
	resp, _ := GetInfo(api)

	fmt.Println("reps:", resp)
}
解釋一下:

>我們通過httptest.NewServer建立了一個測試的http server

>讀請求設定通過變數r *http.Request,寫變數(也就是返回值)通過w http.ResponseWriter

>通過ts.URL來獲取請求的URL(一般都是<http://ip:port>)

>通過r.Method來獲取請求的方法,來測試判斷我們的請求方法是否正確

>獲取請求路徑:r.URL.EscapedPath(),本例中的請求路徑就是"/person"

>獲取請求引數:r.ParseForm,r.Form.Get("addr")

>設定返回的狀態碼:w.WriteHeader(http.StatusOK)

>設定返回的內容(這就是我們想要的結果):w.Write(personResponseBytes),注意w.Write()接收的引數是[]byte,因此需要將object物件列表通過json.Marshal(personResponse)轉換成位元組。

綜上,我們可以通過不傳送httptest來模擬出httpserver和返回值來進行自己程式碼的測試了。

Author:憶之獨秀

Email:[email protected]

相關推薦

Golang單元測試httptest使用

現在有一個需求那就是,我們需要使用Golang的net/http包中的http.Get(url)方法去向伺服器端請求資料,但是負責服務端的同事並沒有將介面實現(可能是同事太忙,把妹,喝酒,扯淡, XO

基於gin的單元測試httptest

目前我們的後端服務提供大量的restful api介面,每次上線都需要測試那邊迴歸一遍這些介面,造成人力的浪費。正好藉著這次單元測試和持續整合,我們引入了httptest框架,結合gin來做介面單元測試。 httptest是golang官方提供的一個包,位於/

golang單元測試mock

搞單元測試,如果碰到這些情況: 1,一個函式,內部包含了很多並且很深的呼叫,但是如果單單測這個函式,其實實現的功能很簡單。 2,一個函式,包含了其他還未實現的呼叫。 3,函式內部對資料的要求極為苛刻。 那麼這時候就可以考慮使用mock來處理。 mock

單元測試Stub和Mock

下載 我們 並且 試用 sample 註入 mes oge new 單元測試之Stub和Mock FROM:http://www.cnblogs.com/TankXiao/archive/2012/03/06/2366073.html 在做單元測試的時候,我們會發現我

python + unittest 做單元測試學習筆記

stl unittest 例子 gin pre log script 有關 assert 單元測試在保證開發效率、可維護性和軟件質量等方面有很重要的地位,所謂的單元測試,就是對一個類,一個模塊或者一個函數進行正確性檢測的一種測試方式。 這裏主要是就應用 python + u

單元測試重要性

表單 曾經 mybatis 服務 request 第一個 完成 分離 能說 單元測試的重要性不言而喻,自我開發生涯以來,從很少註釋過過場場,到非常重視。 單元測試為什麽會讓人忽視呢? 通常情況像一些查詢或者增刪改之類,拿我來說,即便報錯我大概一掃,我就知道錯誤是什麽了,該如

python筆記24-unittest單元測試mock.patch

rom Coding int self. 錯誤 測試用例 方法 org auto 前言 上一篇python筆記23-unittest單元測試之mock對mock已經有初步的認識, 本篇繼續介紹mock裏面另一種實現方式,patch裝飾器的使用,patch() 作為函數裝飾器

補習系列-springboot 單元測試

try 精彩 一次 run rest spec ner hat ltm 目錄 目標 一、About 單元測試 二、About Junit 三、SpringBoot-單元測試 項目依賴 測試樣例 四、Mock測試 五、最後 目標 了解 單元測試的背景 了解如何 利用

單元測試Mock(Moq)

inter 商品 void 簡單 calc 不用 準備 targe ted Mock翻譯為“嘲弄”,其實就是偽造一個對象用於測試。在單元測試中,被測試方法依賴於其他對象時,為了測試簡單一般“偽造”一個這個對象;這樣做的目的: 不用考慮依賴對象的復雜性(方便準備測試數據)

java單元測試如何實現非同步介面的測試案例

測試是軟體釋出的重要環節,單元測試在實際開發中是一種常用的測試方法,java單元測試主要用junit,最新是junit5,本人開發一般用junit4。因為單元測試能夠在軟體模組組合之前儘快發現問題,所以實際開發中投入產出比很高。實際使用難免會遇到非同步操作的介面測試,最常用的情景是別人家的SD

前端單元測試Jest

概述 關於前端單元測試的好處自不必說,基礎的介紹和知識可以參考之前的部落格連結:React Native單元測試。在軟體的測試領域,測試主要分為:單元測試、整合測試和功能測試。 單元測試:在計算機程式設計中,單元測試(英語:Unit Testing)又稱為模組測試, 是針對程式模組(軟體設計的最小單

spring boot單元測試druid NullPointException

最近在使用spring boot 對 Controller 進行單元測試時,發現 druid 竟然丟擲了空指標異常。原因是,使用了druid的監控,需要經過druid的 Filter 攔截器,但是spr

powermockito單元測試深入實踐

概述 由於最近工作需要, 在專案中要做單元測試, 以達到指定的測試用例覆蓋率指標。專案中我們引入的powermockito來編寫測試用例, JaCoCo來監控單元測試覆蓋率。關於框架的選擇, 網上討論mockito和powermockito孰優孰劣的文章眾多, 這裡就不多做闡述, 讀者如有興趣可自行了解。

phpunit 單元測試程式碼覆蓋率

最近團隊在不斷完善專案中的單元測試用例,會用到程式碼覆蓋率分析,本來以為 homestead 應該預設安裝了 xdebug ,所以使用 phpunit --coverage-html ./tests/codeCoverage 來生成 html 報告,但是執行後提示如下錯誤 Error: No

談談單元測試(四):測試工具 TestNG

前言 上一篇文章《測試工具 JUnit 4》中提到了 JUnit 4,並對 JUnit 4 做了簡單的討論,這篇文章我們將要圍繞另一款測試工具討論 —— TestNG。其實,這篇文章應該寫在《測試工具 JUnit 3》之後,和《測試工具 JU

談談單元測試(三):測試工具 JUnit 4

前言 上一篇文章《測試工具 JUnit 3》簡單的討論了 JUnit 3 的使用以及內部的方法。這篇文章將會在 JUnit 3 的基礎上,討論一下 JUnit 4 的新特性。同時,與 JUnit 3 做一個簡單的對比。那麼,廢話就不多說了,直

談談單元測試(二):測試工具 JUnit 3

前言 上一篇文章《 為什麼要進行煩人的單元測試?》討論了一下現階段軟體開發中,程式設計師們測試情況的現狀。這篇文章中,我打算介紹一下單元測試的工具(外掛),並且推薦大家以後在開發中,真正的用上單元測試,用好單元測試。

談談單元測試(一):為什麼要進行煩人的單元測試

前言 最近,在網上看到過一個調查,調查的內容是“程式設計師在專案開發中編寫單元測試的情況”。當然,至於調查的結果,我想聰明的你已經可以猜到了。高達 58.3% 的比例,一般情況下不寫單元測試,只有偶爾的情況才會寫寫。16.6%

Java單元測試JUnit篇

JUnit4通過註解的方式來識別測試方法。目前主要註解有: @BeforeClass 全域性只會執行一次,而且是第一個執行,且必須為static void @Before 在測試方法執行之前執行,即初

Golang單元測試、效能測試

Test 單元測試 testing包提供了對Go包的自動測試支援。 這是和go test 命令相呼應的功能, go test 命令會自動執行所以符合格式 func TestXXX(t *testing.T) 的函式。 Benchmark 效能測試 Functions of the f