Cucumber-like testing for my API [closed] - ruby-on-rails

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have been writing my Rails application with Cucumber in TDD mode: Tests first, then the code. Now my application needs an API. What I like about cucumber is, that I can specify my tests in plain English, so even managers understand what's going on.
Is there any way I can do this for my JSON-API?

YES! This is totally possible. Have you checked out the Cucumber Book by the Pragmatic Programmer series?
Here's a quick example:
Feature: Addresses
In order to complete the information on the place
I need an address
Scenario: Addresses
Given the system knows about the following addresses:
[INSERT TABLE HERE or GRAB FROM DATABASE]
When client requests GET /addresses
Then the response should be JSON:
"""
[
{"venue": "foo", "address": "bar"},
{ more stuff }
]
"""
STEP DEFINITION:
Given(/^the system knows about the following addresses:$/) do |addresses|
# table is a Cucumber::Ast::Table
File.open('addresses.json', 'w') do |io|
io.write(addresses.hashes.to_json)
end
end
When(/^client requests GET (.*)$/) do |path|
#last_response = HTTParty.get('local host url goes here' + path)
end
Then /^the response should be JSON:$/ do |json|
JSON.parse(#last_response.body).should == JSON.parse(json)
end
ENV File:
require File.join(File.dirname(__FILE__), '..', '..', 'address_app')
require 'rack/test'
require 'json'
require 'sinatra'
require 'cucumber'
require 'httparty'
require 'childprocess'
require 'timeout'
server = ChildProcess.build("rackup", "--port", "9000")
server.start
Timeout.timeout(3) do
loop do
begin
HTTParty.get('local host here')
break
rescue Errno::ECONNREFUSED => try_again
sleep 0.1
end
end
end
at_exit do
server.stop
end

You can definitely achieve this. You can write step definitions to assert/verify your json responses. Something like this
Given a username and password
When I try to login via the API
Then I should get logged in
While this works, this just tests the API ( controllers/actions ) work or not, ie more like "functional" testing, not Acceptance testing. As such you are not going to test the API consumer itself.

Related

Rails: save scraped data to seeds.rb [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
As I'm trying to fetch data and have the scraped data saved to the database under seeds.rb, I realized the same data would't overwrite itself. And as a result I got multiple repetitive data in the database.
The goal is to update the existing data with new info instead of creating new ones.
Here's how I fetch the data:
seed.rb
require 'uri'
require 'net/http'
require 'openssl'
require 'json'
url = URI("https://google-flights-search.p.rapidapi.com/search?departure_airport_code=HND&arrival_airport_code=TPE&departure_date=2022-02-17&flight_class=Economy")
http = Net::HTTP.new(url.host, url.port)
http.read_timeout = 300
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(url)
request["x-rapidapi-host"] = ENV["x-rapidapi-host"]
request["x-rapidapi-key"] = ENV["x-rapidapi-key"]
response = http.request(request)
dep_hash = JSON.parse(response.read_body)
I planned to only show the cheapest flight and save it to the database, and expected every time I run rails db:seed the data would be updated and overwritten. (as I set the ticket_id to be the same)
dep_flight_data = dep_hash["flights"]
tempPrice = 10000
depHash= {}
for flight in dep_flight_data
if flight["price"]< tempPrice
tempPrice = flight["price"]
depHash = flight
end
end
dep_ticket_id = (depHash["departure_airport_code"]+depHash["arrival_airport_code"]+ depHash["departure_date"]).split('/').join
Ticket.create(
ticket_id: dep_ticket_id,
departure: depHash["departure_airport_code"],
arrival: depHash["arrival_airport_code"],
departure_date: depHash["departure_date"],
ticket_amount: (depHash["price"]*28)
)
current seed data as below:
Is there any way I can update the seeds file correctly?
Any guides are much appreciated!
Instead of just adding all found tickets into the database you need to check if a similar ticket already exists before creating a new one.
This can be done by changing
Ticket.create(
ticket_id: dep_ticket_id,
departure: depHash["departure_airport_code"],
arrival: depHash["arrival_airport_code"],
departure_date: depHash["departure_date"],
ticket_amount: (depHash["price"]*28)
)
to
Ticket
.create_with(
departure: depHash["departure_airport_code"],
arrival: depHash["arrival_airport_code"],
departure_date: depHash["departure_date"],
ticket_amount: (depHash["price"]*28)
)
.find_or_create_by(ticket_id: dep_ticket_id)
See docs for find_or_create_by

Configuring Ruby On Rails Application With Iex-Ruby-Client Gem

I am a beginner programmer. I recently built an application that uses the iex-ruby-client gem to pull stock quotes for me that I enter into a webpage form. It worked perfectly.
However, in early June, IEX changed their API so that you have to have a publishable token from the IEX cloud console. I got my publishable token from IEX cloud console.
The updated gem docs (https://github.com/dblock/iex-ruby-client) say that I have to "Configure" the application now. I simply don't know how or where I would implement the configuration code. Here is the suggested code from the gem documentation. I just don't know where to put it.
Configure IEX::Api.configure do |config|
config.publishable_token = 'token' # defaults to
ENV['IEX_API_PUBLISHABLE_TOKEN']
config.endpoint = 'https://sandbox.iexapis.com/v1' # defaults to
'https://cloud.iexapis.com/v1'
end
The documents also state, "You can also configure an instance of a client directly."
client = IEX::Api::Client.new(
publishable_token: 'token',
endpoint: 'https://sandbox.iexapis.com/v1'
)
I am adding extra code to clarify what I have done based on the response here. Here is my new config/initializers/iex-ruby-client.rb file (token info isn't the real one).
IEX::Api.configure do |config|
config.publishable_token = 'pk_3b38fsdadfsafjsdalfjdsakfjlda12f519'
config.endpoint = 'https://sandbox.iexapis.com/v1'
end
Here is the relevant method in the controller where I require the library:
def index
require 'iex-ruby-client'
if params[:id] == ""
#nothing = "You forgot to enter a symbol ;)."
elsif
if params[:id]
begin
#stock = IEX::Resources::Quote.get(params[:id])
#company = IEX::Resources::Company.get(params[:id])
rescue StandardError
#error = "That stock symbol doesn't seem to exist. Please enter
another symbol."
end
end
end
end
So I have created the config file and required the gem at the top of the method, but I am still getting an error. I'm sure there is some flaw in my implementation of this token requirement. If you have any additional suggestions, I welcome them. But if this is too much to ask on Stack Overflow, I understand. Thanks.
Well, you clearly have two choices:
use initializer by creating a config file(i.e: iex_client.rb) under the directory /config/initializers and add:
Configure IEX::Api.configure do |config|
config.publishable_token = 'token' # defaults to
ENV['IEX_API_PUBLISHABLE_TOKEN']
config.endpoint = 'https://sandbox.iexapis.com/v1' # defaults to
'https://cloud.iexapis.com/v1'
end
just use the client object wherever you want like this:
client = IEX::Api::Client.new(
publishable_token: 'token',
endpoint: 'https://sandbox.iexapis.com/v1'
)
You probably need to replace token with a correct one. You also need to make sure to require the library wherever you wanna use it.
After unsuccessfully attempting to configure the IEX-ruby-client gem (as described in my question here on stack overflow), I switched over to the stock_quote gem. That gem is built off of the same IEX API, and I had no problems configuring the app with a stock_quote.rb file saved inside config/initializers.

I don't understand testing with savon

I'm looking at the testing docs for Savon here and i don't understand what's going on. I'm fairly new to testing with mocks and stubbing and maybe that's the issue. Here is the example:
require "spec_helper"
# require the helper module
require "savon/mock/spec_helper"
describe AuthenticationService do
# include the helper module
include Savon::SpecHelper
# set Savon in and out of mock mode
before(:all) { savon.mock! }
after(:all) { savon.unmock! }
describe "#authenticate" do
it "authenticates the user with the service" do
message = { username: "luke", password: "secret" }
fixture = File.read("spec/fixtures/authentication_service/authenticate.xml")
# set up an expectation
savon.expects(:authenticate).with(message: message).returns(fixture)
# call the service
service = AuthenticationService.new
response = service.authenticate(message)
expect(response).to be_successful
end
end
end
I understand that we set up an expectation with the fixture i.e. what the response should be.
We then call the service and get a response. My questions are:
1. Is a real call being made?
2. Is this response a real response??
3. Can someone try to explain this overall for me please?
Cheers
No remote request would be made. Since you have mocked authenticate, the response will be short-circuited to your designated value. However, some other preliminary requests might be expected to happen first, like a GET for the WSDL.

[HTTP]how to change "www.example.com" to "localhost:3000"

hi I'm create test use cucumber in my rails apps. in my step scenario I used http basic authenticate, so far it pass the basic authenticate, but when I wanna to call method on controller and post some params, I had problem :
first I use this code in step but failed not cross to method on controller :
post some_admin_url, #params
second I used this code, and failed also, the error is when running the URI.parse redirect to "www.example.com" I want to go "localhost:3000/admin", so I can match the data :
Net::HTTP.post_form(URI.parse(some_admin_url), {'from' => '2005-01-01','to' => '2005-03-31'}) { |i| }
FUTURE :
#selenium
Scenario: Admin want to activate user
Given one user logged in as admin
And admin page
STEPS :
Given /^one user logged in as admin$/ do
create_user_admin
visit '/user_sessions/new'
fill_in 'user_session[login]', :with=>'siadmin'
fill_in 'user_session[password]', :with=>'12345'
click_button 'Anmelden'
end
Given /^admin page$/ do
require "net/http"
require "uri"
uri = URI.parse(user_action_admin_users_url)<br/>
http = Net::HTTP.new(uri.host, uri.port)<br/>
request = Net::HTTP::Get.new(uri.request_uri)<br/>
request.basic_auth("username", "password")<br/>
response = http.request(request)<br/>
end
enter code here
HELP !!!
THANKS
fl00r's answer probably works, but wouldn't catch redirects.
If you want to catch redirects you have to change this in a higher level. For some test frameworks there is a way to set the default host.
Using Capybara we did that:
Capybara.default_host = "subdomain.yourapp.local"
Now we actually use a Rack middleware (in test env only :), that changes changes the env[HTTP_HOST] transparently for the application, so we don't have to care about which testframework / browser driver we use.
What framework do you use?
uname="myapp"
Capybara.default_host = "http://#{uname}.mywebsite.com:3000"
Capybara.server_port = 3000 # could be any of your choice
Capybara.app_host = "http://#{uname}.mywebsite.com:#{Capybara.server_port}"
OR
request.host="www.myapp.com"
,when you are extending ActionController::TestCase
try to change url to path
post some_admin_path, #params
or
post "http://localhost:3000/#{some_admin_url}", #params

Auto-Reply Tweets in Twitter (bot) in simple way or tools?

is any possible & simple way to make a twitter bot that will reply to some tweets (depend on search terms) in certain time interval. can anyone help me.
for example twitter.com/shastribot
Thanks
If you like Ruby, then I suggest using the Twitter gem: https://github.com/jnunemaker/twitter
It makes things very easy.
You could then write a script that checks whether there are any replies to the bot and if there are any new ones sends out a message. Then set it up as a cron job running as often as you think is necessary.
There's also the Twitter Bot interface to Twitter, I haven't used it myself but might be worth a look: http://integrum.rubyforge.org/twitter_bot/
You should try tweebot. It's python micro framework for twitter bots. This lib provides built-in blocks (like Filters, Selectors and Actions) that you can combine to achieve your requirements. For example, next code demonstrates how-to create canonical implementation of "retweet" bot (more examples).
# Next code demonstrates how to create simple twitter bot that select all
# friends' tweets with your mentiones and retweet they.
import tweebot as twb
def main():
# Step 1. setup context configuration
repeater = twb.Context({
'app_name' : 'repeater',
'username' : '<YOUR ACCOUNT NAME>',
'consumer_key' : '<YOUR CONSUMER KEY>',
'consumer_secret' : '<YOUR CONSUMER SECRET>',
'access_key' : '<YOUR ACCESS KEY>',
'access_secret' : '<YOUR ACCESS SECRET>',
'timeout' : 10 * 60, # 10 min, ensure twitter api limits
'history_file' : 'history.json', # don't repeat answered tweets
})
# Step 2. enable pretty logging (stdout by default)
twb.enable_logging(repeater)
# Step 3. setup chain Selector->Filters->Action
chain = (
# Select recently tweets with current user mentions.
twb.SearchMentions(),
# Apply several filters to selected tweets:
twb.MultiPart.And(
# exclude answered, blocked and own tweets
twb.BaseFilter,
# then leave only friends tweets (friends list will be cached)
twb.UsersFilter.Friends(),
# and finally, exclude tweets with invalid content
twb.BadTweetFilter),
# And now, retweet remain tweets
twb.ReplyRetweet)
# Step 4. start processing
repeater.start_forever(*chain)
if __name__ == '__main__':
main()
Ruby's twitter gem is a very good one. You can make use of twitter API to see the available methods.
You can start with a Twitter::REST::Client like following:
twitter_client = Twitter::REST::Client.new do |config|
config.consumer_key = "YOUR_CONSUMER_KEY"
config.consumer_secret = "YOUR_CONSUMER_SECRET"
config.access_token = "YOUR_ACCESS_TOKEN"
config.access_token_secret = "YOUR_ACCESS_SECRET"
end
Then you can you use your twitter_client for various purpose. For example you can post a tweet to your profile using this:
twitter_client.update("I am posting this tweet from my Ruby program")
You can get a list of all tweets by providing the twitter username like this:
twitter_client.user_timeline("YOUR_TWITTER_USER_NAME").each do |tweet|
puts tweet.text
end
For searching for tweets, take a look at this.

Resources