2017-08-18 10 views
1

私のプログラムで漏れが見つかったら、問題を解決しました。しかし、今私は " test"のGoテストで接続が漏れているのを見つけようとしていますか?これは私の質問です。Goテストでリークする接続をテストするにはどうすればよいですか?

私のテストでリクエストの数を変更しようとしましたが問題ではありませんでした。私が何をしても、私のテストでTCP接続の現在の数は同じままです。

func TestLeakingConnections(t *testing.T) { 
    getter := myhttp.New() 

    s := newServer(ok) 
    defer s.Close() 

    cur := tcps(t) 
    for i := 0; i < 1000; i++ { 
     r, _ := getter.GetWithTimeout(s.URL, time.Millisecond*10) 
     r.Body.Close() 
    } 

    for tries := 10; tries >= 0; tries-- { 
     growth := tcps(t) - cur 
     if growth > 5 { 
      t.Error("leaked") 
      return 
     } 
    } 
} 

// find tcp connections 
func tcps(t *testing.T) (conns int) { 
    lsof, err := exec.Command("lsof", "-n", "-p", strconv.Itoa(os.Getpid())).Output() 
    if err != nil { 
     t.Skip("skipping test; error finding or running lsof") 
    } 

    for _, ls := range strings.Split(string(lsof), "\n") { 
     if strings.Contains(ls, "TCP") { 
      conns++ 
     } 
    } 
    return 
} 

func newServer(f http.HandlerFunc) *httptest.Server { 
    return httptest.NewServer(http.HandlerFunc(f)) 
} 

func ok(w http.ResponseWriter, r *http.Request) { 
    w.Header().Add("Content-Type", "application/xml") 
    io.WriteString(w, "<xml></xml>") 
} 

// myhttp package 

// ...other code omitted for clarification 

func (g *Getter) GetWithTimeout(
    url string, 
    timeout time.Duration, 
) (
    *http.Response, error, 
) { 
    // this is the leaking part 
    // moving this out of here will stop leaks 
    transport := http.Transport{ 
     DialContext: (&net.Dialer{ 
      Timeout: timeout, 
     }).DialContext, 
     TLSHandshakeTimeout: timeout, 
     ResponseHeaderTimeout: timeout, 
     ExpectContinueTimeout: timeout, 
    } 

    client := http.Client{ 
     Timeout: timeout, 
     Transport: &transport, 
    } 

    return client.Get(url) 
} 

// fixture worker package 

// some outside code injects getter into fixture_worker like this: 
getter := myhttp.New() 

// NewWithTimeout creates a new fetcher with timeout threshold 
func NewWithTimeout(
    getter myhttp.HTTPGetter, 
    fetchURL string, 
    timeout time.Duration, 
) *Fetcher { 
    return &Fetcher{getter, fetchURL, timeout} 
} 

// Fetch fetches fixture xml 
func (f *Fetcher) Fetch() (*parser.FixtureXML, error) { 
    res, err := f.getter.GetWithTimeout(f.fetchURL, f.timeout) 
    if err != nil { 
     if res != nil && res.Body != nil { 
      res.Body.Close() 
     } 
     return nil, errors.Wrap(err, ErrFetch.Error()) 
    } 
    defer res.Body.Close() 

    ioutil.ReadAll(res.Body) 

    return &parser.FixtureXML{}, nil 
} 

leaking conns gif


治具ワーカーlsofの出力:テストのhttps://pastebin.com/fDUkpYsE

出力:労働者フィクスチャそれが漏れたのに対し、リークしたことがないhttps://pastebin.com/uGpK0czn

テスト。

フィクスチャーワーカーは、テストと同じコードを使用して、myhttpパッケージを使用してhttp取得を要求しています。

+0

あなたは身体をまったく読んでいません.HTMLクライアントは、接続を完全に捨てて節約しています(動作は保証されておらず、信頼できません)。 'GetWithTimeout'は正当な応答があったときにエラー値を追加して、本文を閉じるという文書化されたパターンも破っています。これは、呼び出し側が、 'ErrStatus'を何らかの方法で検査せずに、エラーが発生したときにボディが閉じるかどうかを知る方法がないことを意味します。私は 'dialFunc'の理由を理解していないので、とにかく' DialContext'を使うべきです。 – JimB

+0

良いヒントをいただき、ありがとうございます。しかし、それは私の質問に答えることはできません:どうすれば私のテストで漏れをテストできますか? –

答えて

1

テストで現実を表現したい場合は、テストの外で行うのと同じ方法でテストを使用する必要があります。この場合、あなたはいずれの応答も読んでいないので、再利用できないため、できるだけ早く破棄してしまうので、接続が失われるのを防ぐことができます。

応答を読み取ると、接続が使用され、「リークされた」状態になります。どのような場合でもエラーを適切に処理する必要があります。応答本体は常にClose()にする必要があります。最も一般的なバグが不適切なことから生じるので、HTTP応答を処理し、それが閉じていてください非常に単純であり、必ずしもテストを行う必要はありませんようにするパターン(What could happen if I don't close response.Body in golang?を参照)

resp, err := GetWithTimeout(s.URL) 
    if err != nil { 
     t.Fatal(err) 
    } 
    ioutil.ReadAll(resp.Body) 
    resp.Body.Close() 

これは、間違いなく、限られた有用性でありますエラーとレスポンスの処理が含まれています。テストでは最初に正しくテストする必要があるため、テストしていません。

ここでの残りの問題は、あなたのGetWithTimeoutメソッドがエラー値 HTTPパッケージのドキュメントだけでなく、ほとんどのユーザーの期待に反する有効なHTTPレスポンスを返すということです。エラーを挿入する場合は、同じポイントでレスポンスを処理して、ボディが閉じて破棄されていることを確認することをお勧めします。

最後に、多くのGetWithTimeoutは不必要なときにトランスポートを作成するだけでなく、リクエストごとにhttp.Clientを作成するのは、通常、再利用を目的としており、同時に使用することができるため不要です。

+0

本当に素晴らしいヒント、そのすべてのことに非常に感謝します。私は今これらのすべてを検討しています。しかし、私はまだ統合テストで漏れた接続をテストする方法を見つけることができません。私はその行動をテストする方法についても興味があります。 –

+0

@InancGumus:あなたがやっている 'lsof'チェックはうまくいきます(TIME_WAITのようなものをフィルタリングしてクライアント接続のみを探したいかもしれませんが)。テストでコードを修正すると期待される結果が表示されます。テストしようとしている他の動作は何ですか? – JimB

+0

はい、あります。しかし、私の質問のコードのように、現在、テストでは漏れを検出することはできません。しかし、CLIで自分自身でlsofを実行すると、リークが見えます(私の質問では、付属のgifで見ることができます_)。 –

関連する問題