webmock 0.8.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +113 -0
- data/README.md +84 -18
- data/VERSION +1 -1
- data/lib/webmock.rb +1 -0
- data/lib/webmock/adapters/rspec/webmock_matcher.rb +2 -2
- data/lib/webmock/http_lib_adapters/net_http.rb +11 -13
- data/lib/webmock/request.rb +1 -1
- data/lib/webmock/request_profile.rb +11 -2
- data/lib/webmock/request_signature.rb +8 -2
- data/lib/webmock/request_stub.rb +39 -11
- data/lib/webmock/response.rb +32 -1
- data/lib/webmock/responses_sequence.rb +40 -0
- data/lib/webmock/util/headers.rb +1 -1
- data/lib/webmock/webmock.rb +4 -4
- data/spec/example_curl_output.txt +20 -0
- data/spec/httpclient_spec_helper.rb +4 -0
- data/spec/net_http_spec.rb +8 -0
- data/spec/net_http_spec_helper.rb +10 -1
- data/spec/request_profile_spec.rb +5 -0
- data/spec/request_registry_spec.rb +4 -4
- data/spec/request_signature_spec.rb +35 -0
- data/spec/request_stub_spec.rb +123 -0
- data/spec/response_spec.rb +72 -7
- data/spec/spec_helper.rb +27 -0
- data/spec/webmock_spec.rb +674 -330
- data/test/test_webmock.rb +2 -1
- data/webmock.gemspec +5 -3
- metadata +5 -3
- data/CHANGELOG +0 -36
data/lib/webmock/response.rb
CHANGED
@@ -1,9 +1,19 @@
|
|
1
|
+
#compatibility with Ruby 1.9.2 preview1 to allow reading raw responses
|
2
|
+
class StringIO
|
3
|
+
alias_method :read_nonblock, :sysread
|
4
|
+
end
|
5
|
+
|
1
6
|
module WebMock
|
7
|
+
|
2
8
|
class Response
|
3
9
|
attr_reader :options
|
4
10
|
|
5
11
|
def initialize(options = {})
|
6
|
-
|
12
|
+
if options.is_a?(IO) || options.is_a?(String)
|
13
|
+
self.options = read_raw_response(options)
|
14
|
+
else
|
15
|
+
self.options = options
|
16
|
+
end
|
7
17
|
@options[:headers] = Util::Headers.normalize_headers(@options[:headers]) unless @options[:headers].is_a?(Proc)
|
8
18
|
end
|
9
19
|
|
@@ -39,6 +49,8 @@ module WebMock
|
|
39
49
|
def ==(other)
|
40
50
|
options == other.options
|
41
51
|
end
|
52
|
+
|
53
|
+
private
|
42
54
|
|
43
55
|
def stringify_body!
|
44
56
|
if @options[:body].is_a?(IO)
|
@@ -47,6 +59,25 @@ module WebMock
|
|
47
59
|
io.close
|
48
60
|
end
|
49
61
|
end
|
62
|
+
|
63
|
+
def read_raw_response(raw_response)
|
64
|
+
if raw_response.is_a?(IO)
|
65
|
+
string = raw_response.read
|
66
|
+
raw_response.close
|
67
|
+
raw_response = string
|
68
|
+
end
|
69
|
+
socket = Net::BufferedIO.new(raw_response)
|
70
|
+
response = Net::HTTPResponse.read_new(socket)
|
71
|
+
transfer_encoding = response.delete('transfer-encoding') #chunks were already read by curl
|
72
|
+
response.reading_body(socket, true) {}
|
73
|
+
|
74
|
+
options = {}
|
75
|
+
options[:headers] = {}
|
76
|
+
response.each_header {|name, value| options[:headers][name] = value}
|
77
|
+
options[:headers]['transfer-encoding'] = transfer_encoding if transfer_encoding
|
78
|
+
options[:body] = response.read_body
|
79
|
+
options
|
80
|
+
end
|
50
81
|
|
51
82
|
end
|
52
83
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module WebMock
|
2
|
+
|
3
|
+
class ResponsesSequence
|
4
|
+
|
5
|
+
attr_accessor :times_to_repeat
|
6
|
+
|
7
|
+
def initialize(responses)
|
8
|
+
@times_to_repeat = 1
|
9
|
+
@responses = responses
|
10
|
+
@current_position = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def end?
|
14
|
+
@times_to_repeat == 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def next_response
|
18
|
+
if @times_to_repeat > 0
|
19
|
+
response = @responses[@current_position]
|
20
|
+
increase_position
|
21
|
+
response
|
22
|
+
else
|
23
|
+
@responses.last
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def increase_position
|
30
|
+
if @current_position == (@responses.length - 1)
|
31
|
+
@current_position = 0
|
32
|
+
@times_to_repeat -= 1
|
33
|
+
else
|
34
|
+
@current_position += 1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
data/lib/webmock/util/headers.rb
CHANGED
@@ -7,7 +7,7 @@ module WebMock
|
|
7
7
|
def self.normalize_headers(headers)
|
8
8
|
return nil unless headers
|
9
9
|
array = headers.map { |name, value|
|
10
|
-
[name.to_s.split(/_|-/).map { |segment| segment.capitalize }.join("-"), value.to_s]
|
10
|
+
[name.to_s.split(/_|-/).map { |segment| segment.capitalize }.join("-"), value.is_a?(Regexp) ? value : value.to_s]
|
11
11
|
}
|
12
12
|
Hash[*array.flatten]
|
13
13
|
end
|
data/lib/webmock/webmock.rb
CHANGED
@@ -11,15 +11,15 @@ module WebMock
|
|
11
11
|
RequestProfile.new(method, uri)
|
12
12
|
end
|
13
13
|
|
14
|
-
def assert_requested(method, uri, options = {})
|
14
|
+
def assert_requested(method, uri, options = {}, &block)
|
15
15
|
expected_times_executed = options.delete(:times) || 1
|
16
|
-
request = RequestProfile.new(method, uri, options)
|
16
|
+
request = RequestProfile.new(method, uri, options).with(&block)
|
17
17
|
verifier = RequestExecutionVerifier.new(request, expected_times_executed)
|
18
18
|
assertion_failure(verifier.failure_message) unless verifier.matches?
|
19
19
|
end
|
20
20
|
|
21
|
-
def assert_not_requested(method, uri, options = {})
|
22
|
-
request = RequestProfile.new(method, uri, options)
|
21
|
+
def assert_not_requested(method, uri, options = {}, &block)
|
22
|
+
request = RequestProfile.new(method, uri, options).with(&block)
|
23
23
|
verifier = RequestExecutionVerifier.new(request, options.delete(:times))
|
24
24
|
assertion_failure(verifier.negative_failure_message) unless verifier.does_not_match?
|
25
25
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Content-Type: text/html; charset=UTF-8
|
3
|
+
Connection: Keep-Alive
|
4
|
+
Date: Sat, 23 Jan 2010 01:01:05 GMT
|
5
|
+
Content-Length: 438
|
6
|
+
|
7
|
+
<HTML>
|
8
|
+
<HEAD>
|
9
|
+
<TITLE>Example Web Page</TITLE>
|
10
|
+
</HEAD>
|
11
|
+
<body>
|
12
|
+
<p>You have reached this web page by typing "example.com",
|
13
|
+
"example.net",
|
14
|
+
or "example.org" into your web browser.</p>
|
15
|
+
<p>These domain names are reserved for use in documentation and are not available
|
16
|
+
for registration. See <a href="http://www.rfc-editor.org/rfc/rfc2606.txt">RFC
|
17
|
+
2606</a>, Section 3.</p>
|
18
|
+
</BODY>
|
19
|
+
</HTML>
|
20
|
+
|
@@ -23,6 +23,10 @@ module HTTPClientSpecHelper
|
|
23
23
|
:status => response.code.to_s })
|
24
24
|
end
|
25
25
|
|
26
|
+
def default_client_request_headers(request_method = nil, has_body = false)
|
27
|
+
{'Content-Type'=>'application/x-www-form-urlencoded'} if request_method == 'POST' && has_body
|
28
|
+
end
|
29
|
+
|
26
30
|
def setup_expectations_for_real_request(options = {})
|
27
31
|
socket = mock("TCPSocket")
|
28
32
|
TCPSocket.should_receive(:new).
|
data/spec/net_http_spec.rb
CHANGED
@@ -29,6 +29,13 @@ describe "Webmock with Net:HTTP" do
|
|
29
29
|
req.body = "my_params"
|
30
30
|
Net::HTTP.start("www.example.com") { |http| http.request(req)}.body.should == "abc"
|
31
31
|
end
|
32
|
+
|
33
|
+
it "should handle Net::HTTP::Post#body_stream" do
|
34
|
+
stub_http_request(:post, "www.example.com").with(:body => "my_params").to_return(:body => "abc")
|
35
|
+
req = Net::HTTP::Post.new("/")
|
36
|
+
req.body_stream = StringIO.new("my_params")
|
37
|
+
Net::HTTP.start("www.example.com") { |http| http.request(req)}.body.should == "abc"
|
38
|
+
end
|
32
39
|
|
33
40
|
it "should behave like Net::HTTP and raise error if both request body and body argument are set" do
|
34
41
|
stub_http_request(:post, "www.example.com").with(:body => "my_params").to_return(:body => "abc")
|
@@ -39,4 +46,5 @@ describe "Webmock with Net:HTTP" do
|
|
39
46
|
}.should raise_error("both of body argument and HTTPRequest#body set")
|
40
47
|
end
|
41
48
|
|
49
|
+
|
42
50
|
end
|
@@ -16,11 +16,20 @@ module NetHTTPSpecHelper
|
|
16
16
|
response = http.start {|http|
|
17
17
|
http.request(req, options[:body], &block)
|
18
18
|
}
|
19
|
+
headers = {}
|
20
|
+
response.each_header {|name, value| headers[name] = value}
|
19
21
|
OpenStruct.new({
|
20
22
|
:body => response.body,
|
21
|
-
:headers =>
|
23
|
+
:headers => WebMock::Util::Headers.normalize_headers(headers),
|
22
24
|
:status => response.code })
|
23
25
|
end
|
26
|
+
|
27
|
+
def default_client_request_headers(request_method = nil, has_body = false)
|
28
|
+
default_request = Net::HTTPGenericRequest.new('','','','/')
|
29
|
+
default_net_http_headers = Hash[*default_request.to_hash.map {|k,v|
|
30
|
+
[k, v.flatten]
|
31
|
+
}.flatten]
|
32
|
+
end
|
24
33
|
|
25
34
|
# Sets several expectations that a real HTTP request makes it
|
26
35
|
# past WebMock to the socket layer. You can use this when you need to check
|
@@ -35,6 +35,11 @@ describe RequestProfile do
|
|
35
35
|
"GET http://www.example.com/ with body 'abc' with headers {'A'=>'a', 'B'=>'b'}"
|
36
36
|
end
|
37
37
|
|
38
|
+
it "should report string describing itself with block" do
|
39
|
+
RequestProfile.new(:get, "www.example.com",
|
40
|
+
:body => "abc", :headers => {'A' => 'a', 'B' => 'b'}).with {|req| true}.to_s.should ==
|
41
|
+
"GET http://www.example.com/ with body 'abc' with headers {'A'=>'a', 'B'=>'b'} with given block"
|
42
|
+
end
|
38
43
|
|
39
44
|
describe "with" do
|
40
45
|
before(:each) do
|
@@ -50,7 +50,7 @@ describe RequestRegistry do
|
|
50
50
|
describe "response for request" do
|
51
51
|
|
52
52
|
it "should registered response for request profile" do
|
53
|
-
@request_stub.
|
53
|
+
@request_stub.instance_variable_set(:@responses, [@response = Response.new])
|
54
54
|
RequestRegistry.instance.register_request_stub(@request_stub)
|
55
55
|
RequestRegistry.instance.response_for_request(@request_signature).should == @response
|
56
56
|
end
|
@@ -61,11 +61,11 @@ describe RequestRegistry do
|
|
61
61
|
|
62
62
|
it "should always return last registered matching response" do
|
63
63
|
@request_stub1 = RequestStub.new(:get, "www.example.com")
|
64
|
-
@request_stub1.
|
64
|
+
@request_stub1.instance_variable_set(:@responses, [@response1 = Response.new])
|
65
65
|
@request_stub2 = RequestStub.new(:get, "www.example.com")
|
66
|
-
@request_stub2.
|
66
|
+
@request_stub2.instance_variable_set(:@responses, [@response2 = Response.new])
|
67
67
|
@request_stub3 = RequestStub.new(:get, "www.example.org")
|
68
|
-
@request_stub3.
|
68
|
+
@request_stub3.instance_variable_set(:@responses, [@response3 = Response.new])
|
69
69
|
RequestRegistry.instance.register_request_stub(@request_stub1)
|
70
70
|
RequestRegistry.instance.register_request_stub(@request_stub2)
|
71
71
|
RequestRegistry.instance.register_request_stub(@request_stub3)
|
@@ -89,11 +89,21 @@ describe RequestSignature do
|
|
89
89
|
RequestSignature.new(:get, "www.example.com", :body => "abc").
|
90
90
|
should match(RequestProfile.new(:get, "www.example.com", :body => "abc"))
|
91
91
|
end
|
92
|
+
|
93
|
+
it "should match for body matching regexp" do
|
94
|
+
RequestSignature.new(:get, "www.example.com", :body => "abc").
|
95
|
+
should match(RequestProfile.new(:get, "www.example.com", :body => /^abc$/))
|
96
|
+
end
|
92
97
|
|
93
98
|
it "should not match for different bodies" do
|
94
99
|
RequestSignature.new(:get, "www.example.com", :body => "abc").
|
95
100
|
should_not match(RequestProfile.new(:get, "www.example.com", :body => "def"))
|
96
101
|
end
|
102
|
+
|
103
|
+
it "should not match for body not matching regexp" do
|
104
|
+
RequestSignature.new(:get, "www.example.com", :body => "xabc").
|
105
|
+
should_not match(RequestProfile.new(:get, "www.example.com", :body => /^abc$/))
|
106
|
+
end
|
97
107
|
|
98
108
|
it "should match if other has not specified body" do
|
99
109
|
RequestSignature.new(:get, "www.example.com", :body => "abc").
|
@@ -119,11 +129,21 @@ describe RequestSignature do
|
|
119
129
|
RequestSignature.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpeg'}).
|
120
130
|
should match(RequestProfile.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpeg'}))
|
121
131
|
end
|
132
|
+
|
133
|
+
it "should match for header values matching regexp" do
|
134
|
+
RequestSignature.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpeg'}).
|
135
|
+
should match(RequestProfile.new(:get, "www.example.com", :headers => {'Content-Type' => %r{^image/jpeg$}}))
|
136
|
+
end
|
122
137
|
|
123
138
|
it "should not match for different values of the same header" do
|
124
139
|
RequestSignature.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpeg'}).
|
125
140
|
should_not match(RequestProfile.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/png'}))
|
126
141
|
end
|
142
|
+
|
143
|
+
it "should not match for header values not matching regexp" do
|
144
|
+
RequestSignature.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpegx'}).
|
145
|
+
should_not match(RequestProfile.new(:get, "www.example.com", :headers => {'Content-Type' => %r{^image\/jpeg$}}))
|
146
|
+
end
|
127
147
|
|
128
148
|
it "should match if request has more headers than other" do
|
129
149
|
RequestSignature.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpeg', 'Content-Length' => '8888'}).
|
@@ -165,6 +185,21 @@ describe RequestSignature do
|
|
165
185
|
should_not match(RequestProfile.new(:get, "www.example.com", :headers => {'A'=>'a'}))
|
166
186
|
end
|
167
187
|
|
188
|
+
it "should match if block given to profile evaluates on request to true" do
|
189
|
+
RequestSignature.new(:get, "www.example.com").
|
190
|
+
should match(RequestProfile.new(:get, "www.example.com").with { |request| true } )
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should not match if block given to profile evaluates on request to false" do
|
194
|
+
RequestSignature.new(:get, "www.example.com").
|
195
|
+
should_not match( RequestProfile.new(:get, "www.example.com").with { |request| false } )
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should pass self to the block when matching" do
|
199
|
+
signature = RequestSignature.new(:get, "www.example.com")
|
200
|
+
signature.should match(RequestProfile.new(:get, "www.example.com").with { |request| request == signature } )
|
201
|
+
end
|
202
|
+
|
168
203
|
end
|
169
204
|
|
170
205
|
end
|
data/spec/request_stub_spec.rb
CHANGED
@@ -28,6 +28,11 @@ describe RequestStub do
|
|
28
28
|
@request_stub.request_profile.headers.should == {'B' => 'b'}
|
29
29
|
end
|
30
30
|
|
31
|
+
it "should assign given block to request profile" do
|
32
|
+
@request_stub.with { |req| "block output" }
|
33
|
+
@request_stub.request_profile.with_block.call(nil).should == "block output"
|
34
|
+
end
|
35
|
+
|
31
36
|
end
|
32
37
|
|
33
38
|
describe "to_return" do
|
@@ -38,6 +43,46 @@ describe RequestStub do
|
|
38
43
|
@request_stub.response.status.should == 500
|
39
44
|
end
|
40
45
|
|
46
|
+
it "should assign responses with provided options" do
|
47
|
+
@request_stub.to_return([{:body => "abc"}, {:body => "def"}])
|
48
|
+
[@request_stub.response.body, @request_stub.response.body].should == ["abc", "def"]
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "then" do
|
54
|
+
it "should return stub without any modifications, acting as syntactic sugar" do
|
55
|
+
@request_stub.then.should == @request_stub
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "response" do
|
60
|
+
|
61
|
+
it "should return responses in a sequence passed as array" do
|
62
|
+
@request_stub.to_return([{:body => "abc"}, {:body => "def"}])
|
63
|
+
@request_stub.response.body.should == "abc"
|
64
|
+
@request_stub.response.body.should == "def"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should repeat returning last response" do
|
68
|
+
@request_stub.to_return([{:body => "abc"}, {:body => "def"}])
|
69
|
+
@request_stub.response
|
70
|
+
@request_stub.response
|
71
|
+
@request_stub.response.body.should == "def"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should return responses in a sequence passed as comma separated params" do
|
75
|
+
@request_stub.to_return({:body => "abc"}, {:body => "def"})
|
76
|
+
@request_stub.response.body.should == "abc"
|
77
|
+
@request_stub.response.body.should == "def"
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should return responses declared in multiple to_return declarations" do
|
81
|
+
@request_stub.to_return({:body => "abc"}).to_return({:body => "def"})
|
82
|
+
@request_stub.response.body.should == "abc"
|
83
|
+
@request_stub.response.body.should == "def"
|
84
|
+
end
|
85
|
+
|
41
86
|
end
|
42
87
|
|
43
88
|
describe "to_raise" do
|
@@ -48,7 +93,85 @@ describe RequestStub do
|
|
48
93
|
@request_stub.response.raise_error_if_any
|
49
94
|
}.should raise_error(ArgumentError, "Exception from WebMock")
|
50
95
|
end
|
96
|
+
|
97
|
+
it "should assign sequence of responses with response with exception to be thrown" do
|
98
|
+
@request_stub.to_return(:body => "abc").then.to_raise(ArgumentError)
|
99
|
+
@request_stub.response.body.should == "abc"
|
100
|
+
lambda {
|
101
|
+
@request_stub.response.raise_error_if_any
|
102
|
+
}.should raise_error(ArgumentError, "Exception from WebMock")
|
103
|
+
end
|
51
104
|
|
105
|
+
it "should assign a list responses to be thrown in a sequence" do
|
106
|
+
@request_stub.to_raise(ArgumentError, IndexError)
|
107
|
+
lambda {
|
108
|
+
@request_stub.response.raise_error_if_any
|
109
|
+
}.should raise_error(ArgumentError, "Exception from WebMock")
|
110
|
+
lambda {
|
111
|
+
@request_stub.response.raise_error_if_any
|
112
|
+
}.should raise_error(IndexError, "Exception from WebMock")
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should raise exceptions declared in multiple to_raise declarations" do
|
116
|
+
@request_stub.to_raise(ArgumentError).then.to_raise(IndexError)
|
117
|
+
lambda {
|
118
|
+
@request_stub.response.raise_error_if_any
|
119
|
+
}.should raise_error(ArgumentError, "Exception from WebMock")
|
120
|
+
lambda {
|
121
|
+
@request_stub.response.raise_error_if_any
|
122
|
+
}.should raise_error(IndexError, "Exception from WebMock")
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
describe "times" do
|
129
|
+
|
130
|
+
it "should give error if declared before any response declaration is declared" do
|
131
|
+
lambda {
|
132
|
+
@request_stub.times(3)
|
133
|
+
}.should raise_error("Invalid WebMock stub declaration. times(N) can be declared only after response declaration.")
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should repeat returning last declared response declared number of times" do
|
137
|
+
@request_stub.to_return({:body => "abc"}).times(2).then.to_return({:body => "def"})
|
138
|
+
@request_stub.response.body.should == "abc"
|
139
|
+
@request_stub.response.body.should == "abc"
|
140
|
+
@request_stub.response.body.should == "def"
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should repeat raising last declared exception declared number of times" do
|
144
|
+
@request_stub.to_return({:body => "abc"}).times(2).then.to_return({:body => "def"})
|
145
|
+
@request_stub.response.body.should == "abc"
|
146
|
+
@request_stub.response.body.should == "abc"
|
147
|
+
@request_stub.response.body.should == "def"
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should repeat returning last declared sequence of responses declared number of times" do
|
151
|
+
@request_stub.to_return({:body => "abc"}, {:body => "def"}).times(2).then.to_return({:body => "ghj"})
|
152
|
+
@request_stub.response.body.should == "abc"
|
153
|
+
@request_stub.response.body.should == "def"
|
154
|
+
@request_stub.response.body.should == "abc"
|
155
|
+
@request_stub.response.body.should == "def"
|
156
|
+
@request_stub.response.body.should == "ghj"
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should return self" do
|
160
|
+
@request_stub.to_return({:body => "abc"}).times(1).should == @request_stub
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should raise error if argument is not integer" do
|
164
|
+
lambda {
|
165
|
+
@request_stub.to_return({:body => "abc"}).times("not number")
|
166
|
+
}.should raise_error("times(N) accepts integers >= 1 only")
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should raise error if argument is < 1" do
|
170
|
+
lambda {
|
171
|
+
@request_stub.to_return({:body => "abc"}).times(0)
|
172
|
+
}.should raise_error("times(N) accepts integers >= 1 only")
|
173
|
+
end
|
174
|
+
|
52
175
|
end
|
53
176
|
|
54
177
|
end
|