Yes, I know it is best to use webmock, but I would like to know how to mock this method in RSpec:
def method_to_test
url = URI.parse uri
req = Net::HTTP::Post.new url.path
res = Net::HTTP.start(url.host, url.port) do |http|
http.request req, foo: 1
end
res
end
Here is the RSpec:
let( :uri ) { 'http://example.com' }
specify 'HTTP call' do
http = mock :http
Net::HTTP.stub!(:start).and_yield http
http.should_receive(:request).with(Net::HTTP::Post.new(uri), foo: 1)
.and_return 202
method_to_test.should == 202
end
The test fails because with seems to be trying to match the NET::HTTP::Post object:
RSpec::Mocks::MockExpectationError: (Mock :http).request(#<Net::HTTP::Post POST>, {:foo=>"1"})
expected: 1 time
received: 0 times
Mock :http received :request with unexpected arguments
expected: (#<Net::HTTP::Post POST>, {:foo=>"1"})
got: (#<Net::HTTP::Post POST>, {:foo=>"1"})
How to match properly?
If you don't care about the exact instance, you could use the an_instance_of method:
http.should_receive(:request).with(an_instance_of(Net::HTTP::Post), foo: 1)
.and_return 202
Here it is with new syntax:
before do
http = double
allow(Net::HTTP).to receive(:start).and_yield http
allow(http).to \
receive(:request).with(an_instance_of(Net::HTTP::Get))
.and_return(Net::HTTPResponse)
end
And then in example:
it "http" do
allow(Net::HTTPResponse).to receive(:body)
.and_return('the actual body of response')
# here execute request
end
Very helpful if you need test external api library.
The following snippet works nice for me. It mocks a DELETE request.
mock_net_http = double("Net:HTTP")
mock_net_http_delete = double("Net::HTTP::Delete")
allow(Net::HTTP).to receive(:new).and_return(mock_net_http)
allow(Net::HTTP::Delete).to receive(:new).and_return(mock_net_http_delete)
expect(mock_net_http_delete).to receive(:set_form_data).with(hash_including(some_param: "some-value"))
expect(mock_net_http).to receive(:request).with(mock_net_http_delete)
Related
I have making a post request in Ruby to a slack endpoint and its failing, here is my request, not sure what I'm missing:
#!/usr/bin/env ruby
#Notification Script Test
def send_slack_message
slack_rooms = [ '#test_channel_notify' ]
slack_token_file = (File.join(ENV['HOME'], '.slack_api_token'))
slack_api_token = (File.open(#slack_token_file).readlines)[0].chomp
msg = 'This is a test message send'
slack_url = "https://slack.com/api/chat.postMessage"
%x{curl -k -X POST -d"token=#{slack_api_token}\&channel=#{slack_rooms}\&text=#{msg}" '#{slack_url}'}
end
send_slack_message
I am getting the following error, not sure what I'm missing:
./cap2.rb:7:in `initialize': no implicit conversion of nil into String (TypeError)
from ./cap2.rb:7:in `open'
from ./cap2.rb:7:in `send_slack_message'
from ./cap2.rb:13:in `<main>'
I am a ruby novice so I may be missing everything would love some help!
The error says that you can't give nil to File.open. Make sure #slack_token_file exists and is not nil.
The slack API wants to receive the payload in this format: 'payload={"json": "data"}'
Using Net::HTTP you can make a POST request like this:
require 'net/http'
require 'uri'
def payload(message, channel)
{ channel: channel, username: 'your-username', text: message, icon_emoji: ':robot_face:' }
end
msg = 'This is a test message send'
body = payload(msg, '#test_channel_notify').to_json
url = URI("https://slack.com/api/chat.postMessage")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(url)
request.body = "payload=#{body}"
http.request(request)
While your original problem has been solved, I will provide an alternative solution which follows the more common practice of storing secret credentials as environment variables, not as files (which can, for example, more easily exfiltrated by accident, committed to source control, pasted to a presentation, etc).
Set SLACK_TOKEN in your environment, and then use:
#!/usr/bin/env ruby
#Notification Script Test
def send_slack_message
slack_rooms = [ '#test_channel_notify' ]
slack_api_token = ENV['SLACK_TOKEN']
msg = 'This is a test message send'
slack_url = "https://slack.com/api/chat.postMessage"
%x{curl -k -X POST -d"token=#{slack_api_token}\&channel=#{slack_rooms}\&text=#{msg}" '#{slack_url}'}
end
send_slack_message
I have the following methods in a Ruby script:
def parse_endpoint(endpoint)
return URI.parse(endpoint)
end
def verify_url(endpoint, fname)
url = “#{endpoint}#{fname}”
req = Net::HTTP.new(url.host, url.port)
res = req.request_head(url.path)
if res.code == “200”
true
else
puts “#{fname} is an invalid file”
false
end
end
Testing the url manually like so works fine (returns true since the url is indeed valid):
endpoint = parse_endpoint('http://mywebsite.com/mySubdirectory/')
verify_url(endpoint, “myFile.json”)
However, when I try to do the following in rspec
describe 'my functionality'
let (:endpoint) { parse_endpoint(“http://mywebsite.com/mySubdirectory/”) }
it 'should verify valid url' do
expect(verify_url(endpoint, “myFile.json”).to eq(true))
end
end
it gives me this error
“NoMethodError:
undefined method `host' for "http://mysebsite.com/mySubdirectory/myFile.json":String”
What am I doing wrong?
url is a String object, and you are trying to access a method called host which does not exist in String:
url = “#{endpoint}#{fname}”
req = Net::HTTP.new(url.host, url.port)
EDIT you probably need an URI object. I think this is what you want:
2.2.1 :004 > require 'uri'
=> true
2.2.1 :001 > url = 'http://mywebsite.com/mySubdirectory/'
=> "http://mywebsite.com/mySubdirectory/"
2.2.1 :005 > parsed_url = URI.parse url
=> #<URI::HTTP http://mywebsite.com/mySubdirectory/>
2.2.1 :006 > parsed_url.host
=> "mywebsite.com"
So just add url = URI.parse url before using url.host.
Testing the url manually like so works fine (returns true since the url is indeed valid):
endpoint = parse_endpoint('http://mywebsite.com/mySubdirectory/')
verify_url(endpoint, “myFile.json”)
It seems you missed something when you tested code above (maybe you tested old version) because it can't work as it is now.
Look at these lines of code:
url = "#{endpoint}#{fname}"
req = Net::HTTP.new(url.host, url.port)
You're creating a string variable url from other two variables endpoint and fname. So far, so good.
But then you're trying to access method host on url variable, which doesn't exist (but it exists on the endpoint variable), that's why you get this error.
You may want to use this code instead:
def verify_url(endpoint, fname)
url = endpoint.merge(fname)
res = Net::HTTP.start(url.host, url.port) do |http|
http.head(url.path)
end
# it's actually a bad idea to puts some text in a query method
# let's just return value instead
res.code == "200"
end
I understand that you could use proxy in the ruby Net::HTTP. However, I have no idea how to do this with a bunch of proxy. I need the Net::HTTP to change to another proxy and send another post request after every post request. Also, is it possible to make the Net::HTTP to change to another proxy if the previous proxy is not working? If so, how?
Code I'm trying to implement the script in:
require 'net/http'
sleep(8)
http = Net::HTTP.new('URLHERE', 80)
http.read_timeout = 5000
http.use_ssl = false
path = 'PATHHERE'
data = '(DATAHERE)'
headers = {
'Referer' => 'REFERER HERE',
'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8',
'User-Agent' => '(USERAGENTHERE)'}
resp, data = http.post(path, data, headers)
# Output on the screen -> we should get either a 302 redirect (after a successful login) or an error page
puts 'Code = ' + resp.code
puts 'Message = ' + resp.message
resp.each {|key, val| puts key + ' = ' + val}
puts data
end
Given an array of proxies, the following example will make a request through each proxy in the array until it receives a "302 Found" response. (This isn't actually a working example because Google doesn't accept POST requests, but it should work if you insert your own destination and working proxies.)
require 'net/http'
destination = URI.parse "http://www.google.com/search"
proxies = [
"http://proxy-example-1.net:8080",
"http://proxy-example-2.net:8080",
"http://proxy-example-3.net:8080"
]
# Create your POST request_object once
request_object = Net::HTTP::Post.new(destination.request_uri)
request_object.set_form_data({"q" => "stack overflow"})
proxies.each do |raw_proxy|
proxy = URI.parse raw_proxy
# Create a new http_object for each new proxy
http_object = Net::HTTP.new(destination.host, destination.port, proxy.host, proxy.port)
# Make the request
response = http_object.request(request_object)
# If we get a 302, report it and break
if response.code == "302"
puts "#{proxy.host}:#{proxy.port} responded with #{response.code} #{response.message}"
break
end
end
You should also probably do some error checking with begin ... rescue ... end each time you make a request. If you don't do any error checking and a proxy is down, control will never reach the line that checks for response.code == "302" -- the program will just fail with some type of connection timeout error.
See the Net::HTTPHeader docs for other methods that can be used to customize the Net::HTTP::Post object.
I'm having trouble getting parameters passed in an HTTP Put call, using ruby. Take a look at the "put_data" variable.
When I leave it as a hash, ruby says:
undefined method `bytesize' for #<Hash:0x007fbf41a109e8>
if I convert to a string, I get:
can't convert Net::HTTPUnauthorized into String
I've also tried doing just - '?token=wBsB16NSrfVDpZPoEpM'
def process_activation
uri = URI("http://localhost:3000/api/v1/activation/" + self.member_card_num)
Net::HTTP.start(uri.host, uri.port) do |http|
headers = {'Content-Type' => 'text/plain; charset=utf-8'}
put_data = {:token => "wBsB16NSrfVDpZPoEpM"}
response = http.send_request('PUT', uri.request_uri, put_data, headers)
result = JSON.parse(response)
end
if result['card']['state']['state'] == "active"
return true
else
return false
end
end
I've searched all around, including rubydocs, but can't find an example of how to encode parameters. Any help would be appreciated.
Don't waste your time with NET::HTTP. I used 'rest-client' and had this thing done in minutes...
def process_activation
response = RestClient.put 'http://localhost:3000/api/v1/card_activation/'+ self.member_card_num, :token => "wBsB1pjJNNfiK6NSrfVDpZPoEpM"
result = JSON.parse(response)
return result['card']['state']['state'] == "active"
end
How can I send HTTP GET request with parameters via ruby?
I have tried a lot of examples but all of those failed.
I know this post is old but for the sake of those brought here by google, there is an easier way to encode your parameters in a URL safe manner. I'm not sure why I haven't seen this elsewhere as the method is documented on the Net::HTTP page. I have seen the method described by Arsen7 as the accepted answer on several other questions also.
Mentioned in the Net::HTTP documentation is URI.encode_www_form(params):
# Lets say we have a path and params that look like this:
path = "/search"
params = {q: => "answer"}
# Example 1: Replacing the #path_with_params method from Arsen7
def path_with_params(path, params)
encoded_params = URI.encode_www_form(params)
[path, encoded_params].join("?")
end
# Example 2: A shortcut for the entire example by Arsen7
uri = URI.parse("http://localhost.com" + path)
uri.query = URI.encode_www_form(params)
response = Net::HTTP.get_response(uri)
Which example you choose is very much dependent on your use case. In my current project I am using a method similar to the one recommended by Arsen7 along with the simpler #path_with_params method and without the block format.
# Simplified example implementation without response
# decoding or error handling.
require "net/http"
require "uri"
class Connection
VERB_MAP = {
:get => Net::HTTP::Get,
:post => Net::HTTP::Post,
:put => Net::HTTP::Put,
:delete => Net::HTTP::Delete
}
API_ENDPOINT = "http://dev.random.com"
attr_reader :http
def initialize(endpoint = API_ENDPOINT)
uri = URI.parse(endpoint)
#http = Net::HTTP.new(uri.host, uri.port)
end
def request(method, path, params)
case method
when :get
full_path = path_with_params(path, params)
request = VERB_MAP[method].new(full_path)
else
request = VERB_MAP[method].new(path)
request.set_form_data(params)
end
http.request(request)
end
private
def path_with_params(path, params)
encoded_params = URI.encode_www_form(params)
[path, encoded_params].join("?")
end
end
con = Connection.new
con.request(:post, "/account", {:email => "test#test.com"})
=> #<Net::HTTPCreated 201 Created readbody=true>
I assume that you understand the examples on the Net::HTTP documentation page but you do not know how to pass parameters to the GET request.
You just append the parameters to the requested address, in exactly the same way you type such address in the browser:
require 'net/http'
res = Net::HTTP.start('localhost', 3000) do |http|
http.get('/users?id=1')
end
puts res.body
If you need some generic way to build the parameters string from a hash, you may create a helper like this:
require 'cgi'
def path_with_params(page, params)
return page if params.empty?
page + "?" + params.map {|k,v| CGI.escape(k.to_s)+'='+CGI.escape(v.to_s) }.join("&")
end
path_with_params("/users", :id => 1, :name => "John&Sons")
# => "/users?name=John%26Sons&id=1"