• 《Go Web 编程》之第8章 应用测试


    第8章 应用测试

    8.1 Go与测试

    //包
    testing
    
    //工具
    go test
    
    //文件
    *_test.go
    
    //函数
    func TestXxx(t *testing.T) {
    //方法
    	t.Error
    	t.Fail
    	t.Skip
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    8.2 单元测试

    单元(unit),模块化部分,函数或方法。
    单元测试已测试套件(test suite)形式运行,验证特定行为而创建的单元测试用例集合。

    post.json

    {
      "id" : 1,
      "content" : "Hello World!",
      "author" : {
        "id" : 2,
        "name" : "Sau Sheong"
      },
      "comments" : [
        { 
          "id" : 1, 
          "content" : "Have a great day!", 
          "author" : "Adam"
        },
        {
          "id" : 2, 
          "content" : "How are you today?", 
          "author" : "Betty"
        }
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    main.go

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"os"
    )
    
    type Post struct {
    	Id       int       `json:"id"`
    	Content  string    `json:"content"`
    	Author   Author    `json:"author"`
    	Comments []Comment `json:"comments"`
    }
    
    type Author struct {
    	Id   int    `json:"id"`
    	Name string `json:"name"`
    }
    
    type Comment struct {
    	Id      int    `json:"id"`
    	Content string `json:"content"`
    	Author  string `json:"author"`
    }
    
    // decode JSON from file to struct
    func decode(filename string) (post Post, err error) {
    	jsonFile, err := os.Open(filename)
    	if err != nil {
    		fmt.Println("Error opening JSON file:", err)
    		return
    	}
    	defer jsonFile.Close()
    
    	decoder := json.NewDecoder(jsonFile)
    	err = decoder.Decode(&post)
    	if err != nil {
    		fmt.Println("Error decoding JSON:", err)
    		return
    	}
    	return
    }
    
    func main() {
    	_, err := decode("post.json")
    	if err != nil {
    		fmt.Println("Error:", err)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    main_test.go

    package main
    
    import (
    	"testing"
    )
    
    // Test the decode function
    func TestDecode(t *testing.T) {
    	post, err := decode("post.json")
    	if err != nil {
    		t.Error(err)
    	}
    	if post.Id != 1 {
    		t.Error("Post ID is not the same as post.json", post.Id)
    	}
    	if post.Content != "Hello World!" {
    		t.Error("Post content is not the same as post.json", post.Id)
    	}
    }
    
    // Test the encode function
    func TestEncode(t *testing.T) {
    	t.Skip("Skipping encoding for now")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    testing.T常用函数:

    • Log,类似fmt.Println;
    • Logf,类似fmt.Printf;
    • Fail,标记失败,测序函数继续执行;
    • FailNow,标记失败,测序函数停止执行;
    • Error=Log+Fail
    • Errorf=Logf+Fail
    • Fatal=Log+FailNow
    • Fatalf=Logf+FailNow
    go test
    go test -v -cover
    -v	具体(verbose)标志,更详细的信息
    -cover 覆盖率标志
    
    • 1
    • 2
    • 3
    • 4

    8.2.1 跳过测试用例

    测试驱动开发(test-driven development,TDD)时,测试用例持续失败,直到函数真正地实现,Skip函数可暂时跳过指定的测试用例和耗时的测试用例(完整性检查时,sanity check)。

    短暂标志-short会根据用户编写测试代码的方式,跳过测试部分或全部。

    func TestLongRunningTest(t *testing.T) {
    	if testing.Short() { //加了-short标志为真
    		t.Skip("Skipping long-running test in short mode")
    	}
    	time.Sleep(10*time.Second)
    }
    
    go test -short
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    8.2.2 并行测试

    若单元测试可独立进行,可并行提高测试速度。

    parallel_test.go

    package main
    
    import (
      "testing"
      "time"
    )
    
    func TestParallel_1(t *testing.T) {
      t.Parallel()
      time.Sleep(1 * time.Second)
    }
    
    func TestParallel_2(t *testing.T) {
      t.Parallel()
      time.Sleep(2 * time.Second)
    }
    
    func TestParallel_3(t *testing.T) {
      t.Parallel()
      time.Sleep(3 * time.Second)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    go test -v parallel_test.go
    go test -v parallel_test.go -parallel 3
    //-parallel 3
    //最多并行运行3各测试用例
    
    • 1
    • 2
    • 3
    • 4

    8.2.3 基准测试

    testing包支持两种类型测试:

    • 检验程序功能性的功能测试(functional testing);
    • 查明任务单元性能的基准测试(benchmarking)。
    func BenchmarkXxx(*testing.B){ ... }
    
    • 1
    package main
    
    import (
      "testing"
    )
    
    func BenchmarkDecode(b *testing.B) {
      for i := 0; i < b.N; i++ { //b.N根据代码而改变
        decode("post.json") 
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    go test -v -cover -short -bench .
    //-bench,执行基准测试
    //正则表达式.匹配所有基准测试文件
    //功能测试+基准测试
    
    • 1
    • 2
    • 3
    • 4
    go test -run x -bench .
    //基准测试(+名字为x的功能测试)
    
    • 1
    • 2
    func unmarshal(filename string) (post Post, err error) {
    	jsonFile, err := os.Open(filename)
    	if err != nil {
    		fmt.Println("Error opening JSON file:", err)
    		return
    	}
    	defer jsonFile.Close()
    
    	jsonData, err := ioutil.ReadAll(jsonFile)
    	if err != nil {
    		fmt.Println("Error reading JSON data:", err)
    		return
    	}
    	json.Unmarshal(jsonData, &post)  
      return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    func BenchmarkUnmarshal(b *testing.B) {
      for i := 0; i < b.N; i++ {
        unmarshal("post.json")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    go test -run -x -bench .
    
    • 1

    8.3 HTTP测试

    testing/httptest包可发送HTTP请求,获取HTTP响应。

    httptest包进行HTTP测试的具体步骤:
    (1)创建多路复用器;

    mux := http.NewServerMux()
    
    • 1

    (2)多路复用器绑定测试的处理器;

    mux.HandleFunc("/post/", handleRequest)
    
    • 1

    (3)创建记录器;

    writer := httptest.NewRecorder()
    
    • 1

    (4)创建请求;

    json := strings.NewReader(`{"content":"Updated post","author":"Sau Sheong"}`)
    	request, _ := http.NewRequest("PUT", "/post/1", json)
    
    • 1
    • 2

    (5)发送请求,响应写入记录器;

    mux.ServeHTTP(writer, request)
    
    • 1

    (6)检查记录器中响应结果。

    if writer.Code != 200 {
    	t.Errorf("Response code is %v", writer.Code)
    }
    
    • 1
    • 2
    • 3

    setup.sql

    drop table if exists posts;
    
    create table posts (
      id      serial primary key,
      content text,
      author  varchar(255)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    sql.txt

    psql -h localhost -p 5433 -U gwp -d gwp -f setup.sql
    
    • 1

    data.go

    package main
    
    import (
    	"database/sql"
    	_ "github.com/lib/pq"
    )
    
    var Db *sql.DB
    
    // connect to the Db
    func init() {
    	var err error
    	Db, err = sql.Open("postgres", "port=5433 user=gwp dbname=gwp password=gwp sslmode=disable")
    	if err != nil {
    		panic(err)
    	}
    	
    	//插入记录,用于测试
    	post := &Post{Content: "test", Author: "mali"}
    	post.create()
    }
    
    // Get a single post
    func retrieve(id int) (post Post, err error) {
    	err = Db.QueryRow("select id, content, author from posts where id = $1", id).Scan(&post.Id, &post.Content, &post.Author)
    	return
    }
    
    // Create a new post
    func (post *Post) create() (err error) {
    	statement := "insert into posts (content, author) values ($1, $2) returning id"
    	stmt, err := Db.Prepare(statement)
    	if err != nil {
    		return
    	}
    	defer stmt.Close()
    	
    	err = stmt.QueryRow(post.Content, post.Author).Scan(&post.Id)
    	return
    }
    
    // Update a post
    func (post *Post) update() (err error) {
    	_, err = Db.Exec("update posts set content = $2, author = $3 where id = $1", post.Id, post.Content, post.Author)
    	return
    }
    
    // Delete a post
    func (post *Post) delete() (err error) {
    	_, err = Db.Exec("delete from posts where id = $1", post.Id)
    	return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    server.go

    package main
    
    import (
    	"encoding/json"
    	"net/http"
    	"path"
    	"strconv"
    )
    
    type Post struct {
    	Id      int    `json:"id"`
    	Content string `json:"content"`
    	Author  string `json:"author"`
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/post/", handleRequest)
    	server.ListenAndServe()
    }
    
    // main handler function
    func handleRequest(w http.ResponseWriter, r *http.Request) {
    	var err error
    	switch r.Method {
    	case "GET":
    		err = handleGet(w, r)
    	case "POST":
    		err = handlePost(w, r)
    	case "PUT":
    		err = handlePut(w, r)
    	case "DELETE":
    		err = handleDelete(w, r)
    	}
    	if err != nil {
    		http.Error(w, err.Error(), http.StatusInternalServerError)
    		return
    	}
    }
    
    // Retrieve a post
    // GET /post/1
    func handleGet(w http.ResponseWriter, r *http.Request) (err error) {
    	id, err := strconv.Atoi(path.Base(r.URL.Path))
    	if err != nil {
    		return
    	}
    	post, err := retrieve(id)
    	if err != nil {
    		return
    	}
    	output, err := json.MarshalIndent(&post, "", "\t\t")
    	if err != nil {
    		return
    	}
    	w.Header().Set("Content-Type", "application/json")
    	w.Write(output)
    	return
    }
    
    // Create a post
    // POST /post/
    func handlePost(w http.ResponseWriter, r *http.Request) (err error) {
    	len := r.ContentLength
    	body := make([]byte, len)
    	r.Body.Read(body)
    	var post Post
    	json.Unmarshal(body, &post)
    	err = post.create()
    	if err != nil {
    		return
    	}
    	w.WriteHeader(200)
    	return
    }
    
    // Update a post
    // PUT /post/1
    func handlePut(w http.ResponseWriter, r *http.Request) (err error) {
    	id, err := strconv.Atoi(path.Base(r.URL.Path))
    	if err != nil {
    		return
    	}
    	post, err := retrieve(id)
    	if err != nil {
    		return
    	}
    	len := r.ContentLength
    	body := make([]byte, len)
    	r.Body.Read(body)
    	json.Unmarshal(body, &post)
    	err = post.update()
    	if err != nil {
    		return
    	}
    	w.WriteHeader(200)
    	return
    }
    
    // Delete a post
    // DELETE /post/1
    func handleDelete(w http.ResponseWriter, r *http.Request) (err error) {
    	id, err := strconv.Atoi(path.Base(r.URL.Path))
    	if err != nil {
    		return
    	}
    	post, err := retrieve(id)
    	if err != nil {
    		return
    	}
    	err = post.delete()
    	if err != nil {
    		return
    	}
    	w.WriteHeader(200)
    	return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119

    server_test.go

    package main
    
    import (
    	"encoding/json"
    	"net/http"
    	"net/http/httptest"
    	"strings"
    	"testing"
    )
    
    func TestHandleGet(t *testing.T) {
    	mux := http.NewServeMux()
    	mux.HandleFunc("/post/", handleRequest)
    
    	writer := httptest.NewRecorder()
    	request, _ := http.NewRequest("GET", "/post/1", nil)
    	mux.ServeHTTP(writer, request)
    
    	if writer.Code != 200 {
    		t.Errorf("Response code is %v", writer.Code)
    	}
    	var post Post
    	json.Unmarshal(writer.Body.Bytes(), &post)
    	if post.Id != 1 {
    		t.Errorf("Cannot retrieve JSON post")
    	}
    }
    
    func TestHandlePut(t *testing.T) {
    	mux := http.NewServeMux()
    	mux.HandleFunc("/post/", handleRequest)
    
    	writer := httptest.NewRecorder()
    	json := strings.NewReader(`{"content":"Updated post","author":"Sau Sheong"}`)
    	request, _ := http.NewRequest("PUT", "/post/1", json)
    	mux.ServeHTTP(writer, request)
    
    	if writer.Code != 200 {
    		t.Errorf("Response code is %v", writer.Code)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    package main
    
    import (
    	"encoding/json"
    	"net/http"
    	"net/http/httptest"
    	"os"
    	"strings"
    	"testing"
    )
    
    var mux *http.ServeMux
    var writer *httptest.ResponseRecorder
    
    //setUp()和tearDown()在整个测试过程中只执行一次
    //所有测试用例执行前后执行
    //m.Run()调用所有测试用例
    func TestMain(m *testing.M) {
    	setUp()	//预设(setup)操作
    	code := m.Run()
    	tearDown()	//拆卸(teardown)操作
    	os.Exit(code)
    }
    
    func setUp() {
    	mux = http.NewServeMux()
    	mux.HandleFunc("/post/", handleRequest)
    	writer = httptest.NewRecorder()
    }
    
    func tearDown() {
    }
    
    func TestHandleGet(t *testing.T) {
    	request, _ := http.NewRequest("GET", "/post/1", nil)
    	mux.ServeHTTP(writer, request)
    
    	if writer.Code != 200 {
    		t.Errorf("Response code is %v", writer.Code)
    	}
    	var post Post
    	json.Unmarshal(writer.Body.Bytes(), &post)
    	if post.Id != 1 {
    		t.Errorf("Cannot retrieve JSON post")
    	}
    }
    
    func TestHandlePut(t *testing.T) {
    	json := strings.NewReader(`{"content":"Updated post","author":"Sau Sheong"}`)
    	request, _ := http.NewRequest("PUT", "/post/1", json)
    	mux.ServeHTTP(writer, request)
    
    	if writer.Code != 200 {
    		t.Errorf("Response code is %v", writer.Code)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    测试

    go test .
    
    go run server.go data.go
    curl -i http://127.0.0.1:8080/post/1
    
    • 1
    • 2
    • 3
    • 4

    8.4 测试替身以及依赖注入

    测试替身(test double),模拟测试不方便使用的实际对象、结构或者函数。提高被测试代码的独立性,自动单元测试环境里常使用。

    模拟邮件发送(并不真正发送邮件)或移除单元测试中对真实数据库的依赖,都需要创建测试替身。

    可用依赖注入(dependency injection)设计模式实现测试替身,通过向被调用的对象、结构或者函数传入依赖关系(Go中通常是interface),然后由依赖关系代替被调用者执行实际的操作,依此来解耦软件中的多层(layer)。

    handleRequest和handleGet依赖retrieve函数,后者最终依赖sql.DB,需要移除sql.DB的依赖(避免直接依赖)。

    将sql.DB包含在Post结构体(实现了Text接口)中,然后通过接口Text传递到函数调用流程里,所有调用流程里都未直接依赖sql.DB。

    data.go

    package main
    
    import (
    	"database/sql"
    	_ "github.com/lib/pq"
    )
    
    type Text interface {
      fetch(id int) (err error)
      create() (err error)
      update() (err error)
      delete() (err error)
    }
    
    type Post struct {
    	Db      *sql.DB
    	Id      int    `json:"id"`
    	Content string `json:"content"`
    	Author  string `json:"author"`
    }
    
    // Get a single post
    func (post *Post) fetch(id int) (err error) {
    	err = post.Db.QueryRow("select id, content, author from posts where id = $1", id).Scan(&post.Id, &post.Content, &post.Author)
    	return
    }
    
    // Create a new post
    func (post *Post) create() (err error) {
    	statement := "insert into posts (content, author) values ($1, $2) returning id"
    	stmt, err := post.Db.Prepare(statement)
    	if err != nil {
    		return
    	}
    	defer stmt.Close()
    	
    	err = stmt.QueryRow(post.Content, post.Author).Scan(&post.Id)
    	return
    }
    
    // Update a post
    func (post *Post) update() (err error) {
    	_, err = post.Db.Exec("update posts set content = $2, author = $3 where id = $1", post.Id, post.Content, post.Author)
    	return
    }
    
    // Delete a post
    func (post *Post) delete() (err error) {
    	_, err = post.Db.Exec("delete from posts where id = $1", post.Id)
    	return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    server.go

    package main
    
    import (
    	"database/sql"
    	"encoding/json"
    	_ "github.com/lib/pq"
    	"net/http"
    	"path"
    	"strconv"
    )
    
    func main() {
    	// connect to the Db
    	var err error
    	db, err := sql.Open("postgres", "port=5433 user=gwp dbname=gwp password=gwp sslmode=disable")
    	if err != nil {
    		panic(err)
    	}
    
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/post/", handleRequest(&Post{Db: db}))
    	server.ListenAndServe()
    }
    
    
    // main handler function
    func handleRequest(t Text) http.HandlerFunc {
    	return func(w http.ResponseWriter, r *http.Request) {
    		var err error
    		switch r.Method {
    		case "GET":
    			err = handleGet(w, r, t)
    		case "POST":
    			err = handlePost(w, r, t)
    		case "PUT":
    			err = handlePut(w, r, t)
    		case "DELETE":
    			err = handleDelete(w, r, t)
    		}
    		if err != nil {
    			http.Error(w, err.Error(), http.StatusInternalServerError)
    			return
    		}
    	}
    }
    
    // Retrieve a post
    // GET /post/1
    func handleGet(w http.ResponseWriter, r *http.Request, post Text) (err error) {
    	id, err := strconv.Atoi(path.Base(r.URL.Path))
    	if err != nil {
    		return
    	}
    	err = post.fetch(id)
    	if err != nil {
    		return
    	}
    	output, err := json.MarshalIndent(post, "", "\t\t")
    	if err != nil {
    		return
    	}
    	w.Header().Set("Content-Type", "application/json")
    	w.Write(output)
    	return
    }
    
    // Create a post
    // POST /post/
    func handlePost(w http.ResponseWriter, r *http.Request, post Text) (err error) {
    	len := r.ContentLength
    	body := make([]byte, len)
    	r.Body.Read(body)
    	json.Unmarshal(body, post)
    	err = post.create()
    	if err != nil {
    		return
    	}
    	w.WriteHeader(200)
    	return
    }
    
    // Update a post
    // PUT /post/1
    func handlePut(w http.ResponseWriter, r *http.Request, post Text) (err error) {
    	id, err := strconv.Atoi(path.Base(r.URL.Path))
    	if err != nil {
    		return
    	}
    	err = post.fetch(id)
    	if err != nil {
    		return
    	}
    	len := r.ContentLength
    	body := make([]byte, len)
    	r.Body.Read(body)
    	json.Unmarshal(body, post)
    	err = post.update()
    	if err != nil {
    		return
    	}
    	w.WriteHeader(200)
    	return
    }
    
    // Delete a post
    // DELETE /post/1
    func handleDelete(w http.ResponseWriter, r *http.Request, post Text) (err error) {
    	id, err := strconv.Atoi(path.Base(r.URL.Path))
    	if err != nil {
    		return
    	}
    	err = post.fetch(id)
    	if err != nil {
    		return
    	}
    	err = post.delete()
    	if err != nil {
    		return
    	}
    	w.WriteHeader(200)
    	return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    go run server.go data.go
    
    • 1

    double.go

    package main
    
    type FakePost struct {
    	Id      int
    	Content string
    	Author  string
    }
    
    func (post *FakePost) fetch(id int) (err error) {
    	post.Id = id
    	return
    }
    
    func (post *FakePost) create() (err error) {
    	return
    }
    
    func (post *FakePost) update() (err error) {
    	return
    }
    
    func (post *FakePost) delete() (err error) {
    	return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    server_test.go

    package main
    
    import (
    	"encoding/json"
    	"net/http"
    	"net/http/httptest"
    	"strings"
    	"testing"
    )
    
    func TestGetPost(t *testing.T) {
    	mux := http.NewServeMux()
    	mux.HandleFunc("/post/", handleRequest(&FakePost{}))
    
    	writer := httptest.NewRecorder()
    	request, _ := http.NewRequest("GET", "/post/1", nil)
    	mux.ServeHTTP(writer, request)
    
    	if writer.Code != 200 {
    		t.Errorf("Response code is %v", writer.Code)
    	}
    	var post Post
    	json.Unmarshal(writer.Body.Bytes(), &post)
    	if post.Id != 1 {
    		t.Errorf("Cannot retrieve JSON post")
    	}
    }
    
    func TestPutPost(t *testing.T) {
    	mux := http.NewServeMux()
    	post := &FakePost{}
    	mux.HandleFunc("/post/", handleRequest(post))
    
    	writer := httptest.NewRecorder()
    	json := strings.NewReader(`{"content":"Updated post","author":"Sau Sheong"}`)
    	request, _ := http.NewRequest("PUT", "/post/1", json)
    	mux.ServeHTTP(writer, request)
    
    	if writer.Code != 200 {
    		t.Errorf("Response code is %v", writer.Code)
    	}
    
    	if post.Content != "Updated post" {
    		t.Error("Content is not correct", post.Content)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    go test .
    
    • 1

    8.5 第三方测试库

    8.5.1 Gocheck测试包

    8.5.2 Ginkgo测试框架

  • 相关阅读:
    codeforces刷题二
    核心期刊审稿流程:找对方法,发核心并不难
    Git操作
    使用位运算技巧比较两个数中较大的数
    【猛地学】vxe-table(支持大数据量的可编辑vue表格插件)
    本身您无功而有过,如何天赐百福?
    单商户商城系统功能拆解30—营销中心—积分签到
    阿里大牛解析微服务架构:Docker,Spring全家桶,分布式,数据库
    家庭个人服务器的搭建之路--非傻瓜式描述
    C/C++高精度运算(大整数运算)
  • 原文地址:https://blog.csdn.net/oqqyx1234567/article/details/126745457