@@ -4,15 +4,73 @@ import (
4
4
"bytes"
5
5
"encoding/json"
6
6
"encoding/xml"
7
- "errors"
8
7
"fmt"
9
8
"io"
10
9
"net/http"
11
- "runtime"
12
10
"strconv"
13
11
"strings"
14
12
)
15
13
14
+ // Responder is a callback that receives and http request and returns
15
+ // a mocked response.
16
+ type Responder func (* http.Request ) (* http.Response , error )
17
+
18
+ func (r Responder ) times (name string , n int , fn ... func (... interface {})) Responder {
19
+ count := 0
20
+ return func (req * http.Request ) (* http.Response , error ) {
21
+ count ++
22
+ if count > n {
23
+ err := stackTracer {
24
+ err : fmt .Errorf ("Responder not found for %s %s (coz %s and already called %d times)" , req .Method , req .URL , name , count ),
25
+ }
26
+ if len (fn ) > 0 {
27
+ err .customFn = fn [0 ]
28
+ }
29
+ return nil , err
30
+ }
31
+ return r (req )
32
+ }
33
+ }
34
+
35
+ // Times returns a Responder callable n times before returning an
36
+ // error. If the Responder is called more than n times and fn is
37
+ // passed and non-nil, it acts as the fn parameter of
38
+ // NewNotFoundResponder, allowing to dump the stack trace to localize
39
+ // the origin of the call.
40
+ func (r Responder ) Times (n int , fn ... func (... interface {})) Responder {
41
+ return r .times ("Times" , n , fn ... )
42
+ }
43
+
44
+ // Once returns a new Responder callable once before returning an
45
+ // error. If the Responder is called 2 or more times and fn is passed
46
+ // and non-nil, it acts as the fn parameter of NewNotFoundResponder,
47
+ // allowing to dump the stack trace to localize the origin of the
48
+ // call.
49
+ func (r Responder ) Once (fn ... func (... interface {})) Responder {
50
+ return r .times ("Once" , 1 , fn ... )
51
+ }
52
+
53
+ // Trace returns a new Responder that allow to easily trace the calls
54
+ // of the original Responder using fn. It can be used in conjunction
55
+ // with the testing package as in the example below with the help of
56
+ // (*testing.T).Log method:
57
+ // import "testing"
58
+ // ...
59
+ // func TestMyApp(t *testing.T) {
60
+ // ...
61
+ // httpmock.RegisterResponder("GET", "/foo/bar",
62
+ // httpmock.NewStringResponder(200, "{}").Trace(t.Log),
63
+ // )
64
+ func (r Responder ) Trace (fn func (... interface {})) Responder {
65
+ return func (req * http.Request ) (* http.Response , error ) {
66
+ resp , err := r (req )
67
+ return resp , stackTracer {
68
+ customFn : fn ,
69
+ err : err ,
70
+ }
71
+ }
72
+ }
73
+
16
74
// ResponderFromResponse wraps an *http.Response in a Responder
17
75
func ResponderFromResponse (resp * http.Response ) Responder {
18
76
return func (req * http.Request ) (* http.Response , error ) {
@@ -54,43 +112,18 @@ func NewErrorResponder(err error) Responder {
54
112
// httpmock.RegisterNoResponder(httpmock.NewNotFoundResponder(t.Fatal))
55
113
//
56
114
// Will abort the current test and print something like:
57
- // response:69: Responder not found for: GET http://foo.bar/path
58
- // Called from goroutine 20 [running]:
59
- // github.com/jarcoal/httpmock.NewNotFoundResponder.func1(0xc00011f000, 0x0, 0x42dfb1, 0x77ece8)
60
- // /go/src/github.com/jarcoal/httpmock/response.go:67 +0x1c1
61
- // github.com/jarcoal/httpmock.runCancelable(0xc00004bfc0, 0xc00011f000, 0x7692f8, 0xc, 0xc0001208b0)
62
- // /go/src/github.com/jarcoal/httpmock/transport.go:146 +0x7e
63
- // github.com/jarcoal/httpmock.(*MockTransport).RoundTrip(0xc00005c980, 0xc00011f000, 0xc00005c980, 0x0, 0x0)
64
- // /go/src/github.com/jarcoal/httpmock/transport.go:140 +0x19d
65
- // net/http.send(0xc00011f000, 0x7d3440, 0xc00005c980, 0x0, 0x0, 0x0, 0xc000010400, 0xc000047bd8, 0x1, 0x0)
66
- // /usr/local/go/src/net/http/client.go:250 +0x461
67
- // net/http.(*Client).send(0x9f6e20, 0xc00011f000, 0x0, 0x0, 0x0, 0xc000010400, 0x0, 0x1, 0x9f7ac0)
68
- // /usr/local/go/src/net/http/client.go:174 +0xfb
69
- // net/http.(*Client).do(0x9f6e20, 0xc00011f000, 0x0, 0x0, 0x0)
70
- // /usr/local/go/src/net/http/client.go:641 +0x279
71
- // net/http.(*Client).Do(...)
72
- // /usr/local/go/src/net/http/client.go:509
73
- // net/http.(*Client).Get(0x9f6e20, 0xc00001e420, 0x23, 0xc00012c000, 0xb, 0x600)
74
- // /usr/local/go/src/net/http/client.go:398 +0x9e
75
- // net/http.Get(...)
76
- // /usr/local/go/src/net/http/client.go:370
77
- // foo.bar/foobar/foobar.TestMyApp(0xc00011e000)
78
- // /go/src/foo.bar/foobar/foobar/my_app_test.go:272 +0xdbb
79
- // testing.tRunner(0xc00011e000, 0x77e3a8)
80
- // /usr/local/go/src/testing/testing.go:865 +0xc0
81
- // created by testing.(*T).Run
82
- // /usr/local/go/src/testing/testing.go:916 +0x35a
115
+ // transport_test.go:735: Called from net/http.Get()
116
+ // at /go/src/github.com/jarcoal/httpmock/transport_test.go:714
117
+ // github.com/jarcoal/httpmock.TestCheckStackTracer()
118
+ // at /go/src/testing/testing.go:865
119
+ // testing.tRunner()
120
+ // at /go/src/runtime/asm_amd64.s:1337
83
121
func NewNotFoundResponder (fn func (... interface {})) Responder {
84
122
return func (req * http.Request ) (* http.Response , error ) {
85
- mesg := fmt .Sprintf ("Responder not found for %s %s" , req .Method , req .URL )
86
- if fn != nil {
87
- buf := make ([]byte , 4096 )
88
- n := runtime .Stack (buf , false )
89
- buf = buf [:n ]
90
- fn (mesg + "\n Called from " +
91
- strings .Replace (strings .TrimSuffix (string (buf ), "\n " ), "\n " , "\n " , - 1 ))
123
+ return nil , stackTracer {
124
+ customFn : fn ,
125
+ err : fmt .Errorf ("Responder not found for %s %s" , req .Method , req .URL ),
92
126
}
93
- return nil , errors .New (mesg )
94
127
}
95
128
}
96
129
0 commit comments