I have setup a resque background job that queries the slave database instead of the master. In my resque class I have added code to establish a connection to the slave and then I de-establish the connection at the end of the method. My question is how would I test in rspec that a query is hitting a specific database within a method? Code sample below:
class ResqueJob
#queue = :resque_job
def self.perform(user_id)
ActiveRecord::Base.establish_connection(
:adapter => "mysql2",
:host => "slave_database.com",
:username => "test",
:database => "sample"
)
user = User.find_by_id(current_user_id)
#bunch of code in here
ActiveRecord::Base.clear_active_connections! # Disconnect from slave database
end
end
As a general rule, you shouldn't test behavior of library code. Once you've set the connection, it's up to ActiveRecord to send queries to the right database. Instead, test that your code does its part of the job:
it "sets the database connection to the slave" do
params = {
:adapter => "mysql2",
:host => "slave_database.com",
:username => "test",
:database => "sample"
}
ActiveRecord::Base.should_receive(:establish_connection).with(params)
ActiveRecord::Base.should_receive(:clear_active_connections!)
ResqueJob.perform(user)
end
Related
I want to test a simple sign in flow, which needs a existed user in the test database.
describe 'Signin page' do
before :each do
User.generate(:email => 'automatic_tester#gmail.com', :password => 'palmdrive', :first_name => 'Automatic', :last_name => 'Tester')
end
it 'signs in a user', :js => true do
signin
current_path.should == redirect_path
end
end
def signin
## Action to sign in the user
end
## Use DatabaseCleaner since selenium driver is used
DatabaseCleaner.strategy = :truncation
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before :each do
DatabaseCleaner.start
end
config.after :each do
DatabaseCleaner.clean
end
end
Because the way to create a user is a little bit more complicated, so I defined the User.generate method to create a user. To ensure it actually works, in the rails console with test environment, running User.generate(:email => 'automatic_tester#gmail.com', :password => 'palmdrive', :first_name => 'Automatic', :last_name => 'Tester')it successfully created a user in database. Comment out the before :each, the test suite passed successfully.
But the problem is running the codes above, the test fails. It was due to the user can't be created in the database. Why can't the User.generate method create a user?
In the spec_helper add:
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
solves the issue. Then no need to use the DatabaseCleaner either
Use create instead
User.create(:....)
Note that create will create a record in your database. Writing to the DB is an expensive operation and you may want to avoid that in some cases by using new rather than create.
A better approach is to use factories. See FactoryGirl https://github.com/thoughtbot/factory_girl
I have a rails 3.2.3 application that works fine and delivers devise's created mail messages as, registration confirmation and password forget mail messages to set a new password.
However I have a different places that I want to deliver mail notifications.
This is my Notifiermodel.
# encoding: utf-8
class Notifier < ActionMailer::Base
default from: "no-reply#domain.com"
default to: "admin#domain.com"
def new_post_submitted(post)
#post = post
mail(subject: "Anúncio enviado: #{#post.title}")
end
def new_message(message)
#message = message
mail(subject: "Mensagem de contato: #{#message.subject}")
end
end
and the controller calls:
def create
#message = Message.new(params[:message])
if #message.valid?
Notifier.new_message(#message).deliver
redirect_to(root_path, :notice => "Obrigado. Sua Mensagem enviada com sucesso")
else
flash.now.alert = "Por favor preencha todos os campos."
render :new
end
end
The log output says it was delivered:
Started POST "/contato" for 187.57.102.168 at 2012-08-31 11:28:51 +0900
Processing by ContactController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"TVdmDYCA4x3JVi+9pMcpY7OSU/P1lE9enLiFJlK9W1M=", "message"=>{"name"=>"kleber", "email"=>"test#gmail.com", "subject"=>"teste", "body"=>"hello there"}, "commit"=>"Enviar"}
Rendered notifier/new_message.html.erb (0.1ms)
Sent mail to admin#domain.com (152ms)
Redirected to http://www.domain.com/
Completed 302 Found in 158ms (ActiveRecord: 0.0ms)
And the following configuration on my config/production.rbfile.
ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
:address => "localhost",
:port => 25,
:domain => "domain.com",
:authentication => :login,
:user_name => "admin#domain.com",
:password => "mypassword",
:enable_starttls_auto => false
}
Any clue on what is happening here?
Here are the settings that work in my production.rb file.
ActionMailer::Base.smtp_settings = {
:address => 'mail.domain.com',
:port => 587,
:domain => 'domain.com',
:authentication => :login,
:user_name => 'postmaster#domain.com',
:password => 'password'
}
Not sure you have to specify delivery_method?
ActionMailer::Base.delivery_method = :smtp
I don't have that in my config and everything works properly.
In the rails API http://api.rubyonrails.org/ ActionMailer section there is an option to set a delivery error.
raise_delivery_errors - Whether or not errors should be raised if the email fails to be delivered.
You could try that to further troubleshoot the problem.
First, make sure you haven't turned off deliveries somewhere else in the environment file:
# the default is true anyway, so if you don't see it anywhere in
# the file, you should be okay
config.action_mailer.perform_deliveries = true
I would next try changing the delivery method to either :sendmail or :file. If you have sendmail installed and configured on your system (at least a development system), then you should receive the e-mails you send. I was surprised at one point to discover that sendmail worked out of the box on OS X. I'm not sure if you'd find the same on CentOS.
If you don't have sendmail or don't want to configure it, use the :file delivery method to dump the e-mails to a file on the filesystem.
At this point, if the e-mails don't deliver via sendmail or file, you know there's a problem further up the stack. If they do deliver, then the problem is your SMTP configuration. Try using a known working SMTP server (such as your Gmail account). If that works, then you know it's a problem with the SMTP server running on localhost and not with ActionMailer.
I was looking for some good way to parse mails with rails 3.2.
I did not want to setup external server as R. Bates show in episode: http://railscasts.com/episodes/313-receiving-email-with-mailman
I found simple solution:
Mail.defaults do
retriever_method :pop3, :address => "pop.gmail.com",
:port => 995,
:user_name => '*****#gmail.com',
:password => '*****',
:enable_ssl => true
end
Mail.all.each do |email|
some_email_parser(email)
end
It is fired up as rake task invoked by Cron every 15min.
If you see any disadvantages of that approach let me know.
This code works fine so I leave it like this.
I'm looking for a simple example for a omniauth-facebook, devise, capybara, capybara-mechanize, (maybe with VCR), and rpsec integration test...
Capybara can't connect to to an external website. So capybara-mechanize would be needed. Maybe VCR could speed things up. This link describes how to get test facebook users: https://developers.facebook.com/docs/test_users/
Using a mock, as described in the omniauth docs works well for testing the logic of the model and controller. This requires the following code in specs/helpers/omniauth.rb and calling set_omniauth in the :before of your rspec integration test.
However, it would be nice to have a true integration test that runs against facebook servers. Surely, you'd want to flag such slow tests so they do not run via guard and the default rake task.
# You can read about this gist at: http://wealsodocookies.com/posts/how-to-test-facebook-login-using-devise-omniauth-rspec-and-capybara
# which is for twitter. Below is for facebook
def set_omniauth(opts = {})
default = {:provider => :facebook,
:uuid => "1234",
:facebook => {
:email => "foobar#example.com",
:gender => "Male",
:first_name => "foo",
:last_name => "bar",
:nickname => "foo bar",
:image => 'http://graph.facebook.com/659307629/picture?type=square'
}
}
credentials = default.merge(opts)
provider = credentials[:provider]
user_hash = credentials[provider]
OmniAuth.config.test_mode = true
h = {
'provider' => provider,
'uid' => credentials[:uuid],
'info' => user_hash,
"extra" => {
"info" => user_hash
}
}
OmniAuth.config.mock_auth[provider] = CollectionUtility.deep_stringify_keys(h)
end
def set_invalid_omniauth(opts = {})
credentials = { :provider => :facebook,
:invalid => :invalid_crendentials
}.merge(opts)
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[credentials[:provider]] = credentials[:invalid]
end
With the following code I would like to create a database user. For this I want to use the Active Record Transactions. Sadly my code doesn't work, so is somebody able to help me?
begin
ActiveRecord::Base.transaction("CREATE USER " + #user.name + " IDENTIFIED BY "1234")
rescue => e
puts e.to_s
end
I had a chance to think about this, basically you need to change the database to the system one.
ActiveRecord::Base.establish_connection(
:adapter => "mysql2",
:host => "localhost",
:username => "me",
:password => "secret",
:database => "activerecord"
)
:database will be the name of the database that contains the User table.
Then you can wrap the User creation in a transaction, you would do it like this:
ActiveRecord::Base.transaction do
User.create({:name => "my_name"})
end
The attributes used should match what you find in the system User table.
There are probably things you should do like closing connections and such before you do this, but that depends on where you are calling this from.