diff --git a/spec/webmock_spec.cr b/spec/webmock_spec.cr index 77bbe82..aba8d8c 100644 --- a/spec/webmock_spec.cr +++ b/spec/webmock_spec.cr @@ -126,7 +126,7 @@ describe WebMock do it "stubs and calls block for response" do WebMock.wrap do WebMock.stub(:post, "www.example.com").with(body: "Hello!").to_return do |request| - headers = HTTP::Headers.new.merge!({ "Content-length" => "6" }) + headers = HTTP::Headers.new.merge!({"Content-length" => "6"}) HTTP::Client::Response.new(418, body: request.body.to_s.reverse, headers: headers) end @@ -329,6 +329,120 @@ describe WebMock do end end + it "contains registered stubs on failure" do + WebMock.wrap do + WebMock.stub(:get, "https://test.example.com") + begin + HTTP::Client.get("https://www.example.com/") + rescue ex : WebMock::NetConnectNotAllowedError + ex.message.not_nil!.strip.should eq( + <<-MSG + Real HTTP connections are disabled. Unregistered request: GET https://www.example.com/ with headers {"Connection" => "close", "Host" => "www.example.com"} + + You can stub this request with the following snippet: + + WebMock.stub(:get, "https://www.example.com/"). + to_return(body: "") + + Registered request stubs: + + stub(:get, "https://test.example.com") + MSG + ) + end + end + end + + it "contains registered stubs on failure (with headers)" do + WebMock.wrap do + WebMock.stub(:post, "https://test.example.com").with(headers: {"Content-Type" => "application/json"}) + begin + HTTP::Client.get("https://www.example.com/") + rescue ex : WebMock::NetConnectNotAllowedError + ex.message.not_nil!.strip.should eq( + <<-MSG + Real HTTP connections are disabled. Unregistered request: GET https://www.example.com/ with headers {"Connection" => "close", "Host" => "www.example.com"} + + You can stub this request with the following snippet: + + WebMock.stub(:get, "https://www.example.com/"). + to_return(body: "") + + Registered request stubs: + + stub(:post, "https://test.example.com"). + with( + headers: { + "Content-Type" => "application/json" + } + ) + ) + MSG + ) + end + end + end + + it "contains registered stubs on failure (with body)" do + WebMock.wrap do + WebMock.stub(:post, "https://test.example.com").with(body: "line1\nline2") + begin + HTTP::Client.get("https://www.example.com/") + rescue ex : WebMock::NetConnectNotAllowedError + ex.message.not_nil!.strip.should eq( + <<-MSG + Real HTTP connections are disabled. Unregistered request: GET https://www.example.com/ with headers {"Connection" => "close", "Host" => "www.example.com"} + + You can stub this request with the following snippet: + + WebMock.stub(:get, "https://www.example.com/"). + to_return(body: "") + + Registered request stubs: + + stub(:post, "https://test.example.com"). + with( + body: "line1\\nline2" + ) + ) + MSG + ) + end + end + end + + it "contains registered stubs on failure (with headers and body)" do + WebMock.wrap do + WebMock.stub(:post, "https://test.example.com").with(body: "line1\nline2", headers: {"Content-Type" => "application/json", "Accept" => "*/*"}) + begin + HTTP::Client.get("https://www.example.com/") + rescue ex : WebMock::NetConnectNotAllowedError + ex.message.not_nil!.strip.should eq( + <<-MSG + Real HTTP connections are disabled. Unregistered request: GET https://www.example.com/ with headers {"Connection" => "close", "Host" => "www.example.com"} + + You can stub this request with the following snippet: + + WebMock.stub(:get, "https://www.example.com/"). + to_return(body: "") + + Registered request stubs: + + stub(:post, "https://test.example.com"). + with( + body: "line1\\nline2", + headers: { + "Content-Type" => "application/json", + "Accept" => "*/*" + } + ) + ) + MSG + ) + end + end + end + it "works with request callbacks" do WebMock.wrap do WebMock.stub(:get, "http://www.example.com").with(query: {"foo" => "bar"}) @@ -388,7 +502,7 @@ describe WebMock do end # Commented so that specs run fast, but uncomment to try it (it works) - #it "calls callback after live request" do + # it "calls callback after live request" do # WebMock.wrap do # WebMock.callbacks.add do # after_live_request do |request, response| @@ -398,15 +512,15 @@ describe WebMock do # WebMock.allow_net_connect = true # HTTP::Client.get("http://www.example.net") # end - #end + # end - #it "doesn't error if callback is not set" do + # it "doesn't error if callback is not set" do # WebMock.wrap do # WebMock.allow_net_connect = true # client = HTTP::Client.get("http://www.example.net") # client.status_code.should eq 200 # end - #end + # end # it "allows net connect" do # WebMock.wrap do diff --git a/src/webmock.cr b/src/webmock.cr index ce5210d..411a56e 100644 --- a/src/webmock.cr +++ b/src/webmock.cr @@ -33,6 +33,10 @@ module WebMock @@registry.find_stub(request) end + def stubs + @@registry.stubs + end + def callbacks @@callbacks end diff --git a/src/webmock/net_connect_not_allowed_error.cr b/src/webmock/net_connect_not_allowed_error.cr index 5e2a751..9f01de2 100644 --- a/src/webmock/net_connect_not_allowed_error.cr +++ b/src/webmock/net_connect_not_allowed_error.cr @@ -13,6 +13,7 @@ class WebMock::NetConnectNotAllowedError < Exception io << "\n\n" stubbing_instructions(request, io) io << "\n\n" + registered_requests(io) end end @@ -61,6 +62,14 @@ class WebMock::NetConnectNotAllowedError < Exception io << %[ to_return(body: "")] end + private def registered_requests(io) + return if WebMock.stubs.empty? + io << "Registered request stubs:" + io << "\n\n" + WebMock.stubs.each { |stub| io << StubSnippet.new(stub).to_s } + io << "\n\n" + end + private def request_uri_to_s(request, io) io << request.scheme io << "://" diff --git a/src/webmock/stub.cr b/src/webmock/stub.cr index f40d869..f34b6be 100644 --- a/src/webmock/stub.cr +++ b/src/webmock/stub.cr @@ -1,4 +1,6 @@ class WebMock::Stub + getter :method, :uri, :expected_headers, :expected_body, :expected_query + @method : String @uri : URI @expected_headers : HTTP::Headers? diff --git a/src/webmock/stub_registry.cr b/src/webmock/stub_registry.cr index 789660c..503f38e 100644 --- a/src/webmock/stub_registry.cr +++ b/src/webmock/stub_registry.cr @@ -1,4 +1,6 @@ struct WebMock::StubRegistry + getter :stubs + def initialize @stubs = [] of Stub end diff --git a/src/webmock/stub_snippet.cr b/src/webmock/stub_snippet.cr new file mode 100644 index 0000000..093f001 --- /dev/null +++ b/src/webmock/stub_snippet.cr @@ -0,0 +1,35 @@ +struct WebMock::StubSnippet + def initialize(stub : Stub) + @stub = stub + end + + def to_s + String.build do |io| + io << "stub(:#{@stub.method.downcase}, \"#{@stub.uri}\"" + if body || headers + io << ").\n with(\n" + if body + io << " body: \"#{body.to_s.gsub("\n", "\\n")}\"" + io << ",\n" if headers + end + if headers + io << " headers: {\n " + io << headers.not_nil!.map { |key, values| "\"#{key}\" => \"#{values.join(", ")}\"" }.join(",\n ") + io << "\n }\n" + else + io << "\n" + end + io << " )\n" + end + io << ")\n" + end + end + + private def body + @stub.expected_body + end + + private def headers + @stub.expected_headers + end +end