Як перевірити http-дзвінки в Go за допомогою httptest


76

У мене є такий код:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "time"
)

type twitterResult struct {
    Results []struct {
        Text     string `json:"text"`
        Ids      string `json:"id_str"`
        Name     string `json:"from_user_name"`
        Username string `json:"from_user"`
        UserId   string `json:"from_user_id_str"`
    }
}

var (
  twitterUrl = "http://search.twitter.com/search.json?q=%23UCL"
  pauseDuration = 5 * time.Second
)

func retrieveTweets(c chan<- *twitterResult) {
    for {
        resp, err := http.Get(twitterUrl)
        if err != nil {
            log.Fatal(err)
        }

        defer resp.Body.Close()
        body, err := ioutil.ReadAll(resp.Body)
        r := new(twitterResult) //or &twitterResult{} which returns *twitterResult
        err = json.Unmarshal(body, &r)
        if err != nil {
            log.Fatal(err)
        }
        c <- r
        time.Sleep(pauseDuration)
    }

}

func displayTweets(c chan *twitterResult) {
    tweets := <-c
    for _, v := range tweets.Results {
        fmt.Printf("%v:%v\n", v.Username, v.Text)
    }

}

func main() {
    c := make(chan *twitterResult)
    go retrieveTweets(c)
    for {
        displayTweets(c)
    }

}

Я хотів би написати кілька тестів для цього, але я не впевнений, як використовувати пакет httptest http://golang.org/pkg/net/http/httptest/, я б оцінив деякі вказівки

Я це придумав (безсоромно скопійовано з тестів для go OAuth https://code.google.com/p/goauth2/source/browse/oauth/oauth_test.go ):

var request = struct {
    path, query       string // request
    contenttype, body string // response
}{
    path:        "/search.json?",
    query:       "q=%23Kenya",
    contenttype: "application/json",
    body:        twitterResponse,
}

var (
    twitterResponse = `{ 'results': [{'text':'hello','id_str':'34455w4','from_user_name':'bob','from_user_id_str':'345424'}]}`
)

func TestRetrieveTweets(t *testing.T) {
    handler := func(w http.ResponseWriter, r *http.Request) {

        w.Header().Set("Content-Type", request.contenttype)
        io.WriteString(w, request.body)
    }

    server := httptest.NewServer(http.HandlerFunc(handler))
    defer server.Close()

    resp, err := http.Get(server.URL)
    if err != nil {
        t.Fatalf("Get: %v", err)
    }
    checkBody(t, resp, twitterResponse)
}

func checkBody(t *testing.T, r *http.Response, body string) {
    b, err := ioutil.ReadAll(r.Body)
    if err != nil {
        t.Error("reading reponse body: %v, want %q", err, body)
    }
    if g, w := string(b), body; g != w {
        t.Errorf("request body mismatch: got %q, want %q", g, w)
    }
}

Відповіді:


91

httptest проводить два типи тестів: відповідь та сервер

Тест відповіді:

func TestHeader3D(t *testing.T) {
    resp := httptest.NewRecorder()

    uri := "/3D/header/?"
    path := "/home/test"
    unlno := "997225821"

    param := make(url.Values)
    param["param1"] = []string{path}
    param["param2"] = []string{unlno}

    req, err := http.NewRequest("GET", uri+param.Encode(), nil)
    if err != nil {
            t.Fatal(err)
    }

    http.DefaultServeMux.ServeHTTP(resp, req)
    if p, err := ioutil.ReadAll(resp.Body); err != nil {
            t.Fail()
    } else {
            if strings.Contains(string(p), "Error") {
                    t.Errorf("header response shouldn't return error: %s", p)
            } else if !strings.Contains(string(p), `expected result`) {
                    t.Errorf("header response doen't match:\n%s", p)
            }
    }
}

Тест сервера (що вам потрібно використовувати):

func TestIt(t *testing.T){
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        fmt.Fprintln(w, `{"fake twitter json string"}`)
    }))
    defer ts.Close()

    twitterUrl = ts.URL
    c := make(chan *twitterResult)
    go retrieveTweets(c)

    tweet := <-c
    if tweet != expected1 {
        t.Fail()
    }
    tweet = <-c
    if tweet != expected2 {
        t.Fail()
    }
}

До речі, вам не потрібно передавати покажчик на r, оскільки це вже вказівник.

err = json.Unmarshal(body, r)

РЕДАКТУВАТИ: для мого тесту на рекордері я міг використати свій обробник http так:

handler(resp, req)

Але в моєму вихідному коді не використовується мультиплексор за замовчуванням (а з Gorilla / mux), і у мене є деяка обгортка навколо мультиплексу, наприклад, вставка журналу сервера та додавання контексту запиту (Gorilla / context), тому мені довелося починати з mux та виклик ServeHTTP


12
Будь ласка, додайте повний код, наприклад, імпорт усіх пакетів. Новачок тут;) Дякую!
Marçal Juan

8
Ви можете використовувати гоімпорт
030,

9

Спочатку цей фрагмент коду був знайдений на GitHub Gist , але намагаючись застосувати концепцію до одного зі своїх проектів, я зрозумів, що мені довелося суттєво змінити основний код, і я вирішив перевірити ці виклики за допомогою інтеграційного тесту, використовуючи docker і curl.


8

Якщо ви хочете протестувати свою програму, часто найкраще писати її з огляду на тестування. Наприклад, якщо ви витягли внутрішній цикл своєї retrieveTweetsфункції приблизно в такий спосіб:

func downloadTweets(tweetsUrl string) (*twitterResult, error)

Ви можете викликати його за допомогою URL-адреси тестового сервера, який ви налаштували за допомогою httptestпакета, не турбуючись про сплячий режим або повторні запити.


Чи можу я висміяти сервер? Я думаю, що замочування тестового сервера заперечує мету тесту, якщо я можу знущатись над сервером і відповідь, яку я очікую, добре впишеться в тест
jwesonga

4
Цей httptestпакет є інфраструктурою для налаштування невеликого HTTP-сервера для тестів. Ви можете реалізувати обробники запитів так само, як зазвичай, а потім запустити свій код на цьому сервері, а не на Twitter.
Джеймс Хенстрідж

@JamesHenstridge дякую, чудова відповідь! Ви коментуєте тут, є більш інформативним, ніж Оглядова заява в офіційних документах для мережі / http / httptest
Роб
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.