# 12.8.1 测试 HTTP handler

在这节我们将学习如果用 Go 测试 HTTP handlers，这是一个用 Go 测试的特别例子。我们将已 `www.go` 的代码为基础，修改需要调整的地方。

新版本的 `www.go` 命名为 `testWWW.go`，并分为三部分展示。`testWWW.go` 的第一部分代码如下：

```go
package main

import (
    "fmt"
    "net/http"
    "os"
)

func CheckStatusOK(w http.ResponseWriter, r *http.Request){
    w.WriteHeader(http.StatusOK)
    fmt.Fprintf(w, `Fine!`)
}
```

`testWWW.go` 的第二部分代码如下：

```go
func StatusNotFound(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Serving: %s\n", r.URL.Path)
    fmt.Printf("Served: %s\n", r.Host)
}

func MyHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Serving: %s\n", r.URL.Path)
    fmt.Printf("Served: %s\n", r.Host)
}
```

`testWWW.go` 余下代码如下：

```go
func main() {
    PORT := ":8001"
    arguments := os.Args
    if len(arguments) == 1 {
        fmt.Println("Using default port number:", PORT)
    } else {
        PORT = ":" + arguments[1]
    }

    http.HandleFunc("/CheckStatusOK", CheckStatusOK)
    http.HandleFunc("/StatusNotFound", StatusNotFound)
    http.HandleFunc("/", MyHandler)

    err := http.ListenAndServe(PORT, nil)
    if err != nil {
        fmt.Println(err)
        return
    }
}
```

我们现在需要开始测试 `testWWW.go` 了，为此我们应该创建一个 `testWWW_test.go` 文件。这个文件的内容由四部分展示。

`testWWW_test.go` 的第一部分包容如下代码：

```go
package main

import (
    "fmt"
    "net/http"
    "net/http/httptest"
    "testing"
)
```

注意，为了用 Go 测试 web 应用，您需要引入 `net/http/httptest` 标准包。

`testWWW_tesst.go` 的第二部分如下：

```go
func TestCheckStatusOK(t *testing.T) {
    req, err := http.NewRequest("GET", "/CheckStatusOK", nil)
    if err != nil {
        fmt.Println(err)
        return
    }

    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(CheckStatusOK)
    handler.ServeHTTP(rr, req)
```

`httptest.NewRecorder()` 函数返回一个 `httptest.ResponseRecorder` 对象，它被用于记录 HTTP 响应。

`testWWW_test.go` 的第三部分如下：

```go
    status := rr.Code
    if status != http.StatusOK {
        t.Errorf("handler returned %v", status)
    }

    expect := `Fine!`
    if rr.Body.String() != expect {
        t.Errorf("handler returned %v", rr.Body.String())
    }
}
```

您首先检查返回码是所期望的，然后再验证返回消息体也是您期望的。

`testWWW_test.go` 的余下代码如下：

```go
func TestStatusNotFound(t *testing.T) {
    req, err := http.NewRequest("GET", "/StatusNotFound", nil)
    if err != nil {
        fmt.Println(err)
        return
    }

    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(StatusNotFound)
    handler.ServeHTTP(rr, req)

    status := rr.Code
    if status != http.StatusNotFound {
        t.Errorf("handler returned %v", status)
    }
}
```

这个测试函数验证 `main` 包中的 `StatusNotFound()` 函数是否如期运行。

执行 `testWWW_test.go` 的这俩个测试函数将产生如下输出：

```
$ go test testWWW.go testWWW_test.go -v
=== RUN     TestCheckStatusOK
--- PASS:   TestCheckStatusOK (0.00s)
=== RUN     TestStatusNotFound
--- PASS:   TestStatusNotFound (0.00s)
PASS
ok      command-line-arguments  (cached)
```
