Apparently this method exists and I've been trying to use it, but it doesn't seem to work in my use case.
test "should get appropriate autocomplete results upon ideal request" do
get :index, { "term" => "al", "max_results" => "20"}
assert_response :success
assert_not_nil assigns(:payers)
assert json_response.length <= 20
assert_block do
json_response.each do |payer_hash|
return false unless payer_hash['label'][0..1] == "al"
end
true
end
end
That is the test. At first I thought I was using it wrong because I have a block inside of it. But, at the same time, if what I had inside was a simple one-liner, I could just use assert. Am I using it wrong? Is it deprecated in Rails 4.1? If so, what is the new way to do this?
Related
Just wondering how to test that actionmailer requests are actually sent to the delayed_job que in rspec.
I would have assumed it was quite simple, but my delayed_job queue doesn't seem to be incrementing. Code below:
Controller:
def create
#contact = Contact.new(params[:contact])
if #contact.save
contactmailer = ContactMailer
contactmailer.delay.contact_message(#contact)
redirect_to(contacts_url)
else
render :action => "new"
end
Spec:
it "queues mail when a contact is created" do
expectedcount = Delayed::Job.count + 1
Contact.stub(:new).with(mock_contact()) { mock_contact(:save => true) }
post :create, :contact => mock_contact
expectedcount.should eq(Delayed::Job.count)
end
Both before and after the call to the controller, the Delayed::Job.count returns 0. I've tried taking the conditional out of the controller, but I still can't get the delayed job count to increment.
Any suggestions appreciated - cheer
You can also test what the jobs will do by running them or turning off queuing.
Tweak config whenever you want (i.e. in a before :each block).
Delayed::Worker.delay_jobs = false
or perform your saved jobs
Delayed::Worker.new.work_off.should == [1, 0]
I have been using this method happily for a while. For one thing, using the new any_instance support in RSpec, you can test your delayed methods effects directly. However, I've found tests that use work_off to be slow.
What I usually do now is:
mock_delay = double('mock_delay').as_null_object
MyClass.any_instance.stub(:delay).and_return(mock_delay)
mock_delay.should_receive(:my_delayed_method)
Then I have a separate spec for my_delayed_method. This is much faster, and probably better unit testing practice -- particularly for controllers. Though if you're doing request specs or other integration-level specs, then you probably still want to use work_off.
I think your mock object is somehow introducing an error -- it's hard to tell exactly how without seeing the definition of the mock_contact method.
In any case, you might try something along these lines:
it "queues mail when a contact is created" do
Contact.stub(:new) { mock_model(Contact,:save => true) }
Delayed::Job.count.should == 0
post :create, {}
Delayed::Job.count.should == 1
end
or the sexier version (caveat: I always end up doing it the non-sexy way):
it "queues mail when a contact is created" do
Contact.stub(:new) { mock_model(Contact,:save => true) }
expect {
post :create, {}
}.to change(Delayed::Job.count).by(1)
end
You can also follow the convention (from Railscast 275) of
ActionMailer::Base.deliveries.last.to.should == user.email
but instead do this:
Delayed::Job.last.handler.should have_content(user.email)
This thread is a bit old, but here is my go at it:
Create a function expect_jobs
def expect_jobs n, time = nil
expect(Delayed::Job.count).to eq(n)
Timecop.travel(time) unless time.nil?
successes, failures = Delayed::Worker.new.work_off
expect(successes).to eq(n)
expect(failures).to eq(0)
expect(Delayed::Job.count).to eq(0)
Timecop.travel(Time.now) unless time.nil?
end
Then simply call it before checking if the callback has done its job. eg:
it "sends a chapter to the admin user" do
post :chapter_to_user, { chapter: #book.chapters.first}
expect_jobs(1)
SubscribeMailer.should have(1).delivery
SubscribeMailer.deliveries.should have(1).attachment
end
This seems to work on my side, and allows me to run both my delayed jobs and my methods.
#zetetic I think we have to pass block in change method here.
It shoulb be like this:
it "queues mail when a contact is created" do
Contact.stub(:new) { mock_model(Contact,:save => true) }
expect {
post :create, {}
}.to change { Delayed::Job.count }.by(1)
end
I'm trying to test out a controller action on Rails 2.3.10 that connect to Google to retrieve contacts. I'm using Rspec and Mocha for testing. I want to stub out the actual call to Google since this is a unit test. I want to verify that the authsub_url method is called with the correct parameters. Stubbing the method out causes the expectation to fail.
Any advice would be appreciated.
Thanks!
My method that sets up the client to google is below:
def setup_client
#client = GData::Client::DocList.new(:authsub_scope => CONTACTS_SCOPE, :source => 'google-DocListManager-v1.1', :version => '3.0')
if params[:token].nil? && session[:google_token].nil?
#authsub_link = #client.authsub_url(import_method_gmail_url, false, true)
render :action => :index, :layout => "empty"
elsif params[:token] && session[:google_token].nil?
#client.authsub_token = params[:token]
session[:google_token] = #client.auth_handler.upgrade
end
#client.authsub_token = session[:google_token] if session[:google_token]
end
Here is my test:
describe "setup_client" do
it "has a authsub_link if there is no token parameter and the google token is not present in the session" do
GData::Client::DocList.any_instance.stubs(:authsub_url).returns("http://test.google.com/contacts")
user = Factory(:subscriber_user)
profile = Factory(:profile, :user => user)
login_as user
controller.instance_variable_get(:#client).expects(:authsub_url).with(import_method_gmail_url, false, true).once
get :index
assigns(:authsub_link).should == "http://test.google.com/contacts"
end
end
I would recommend FakeWeb. It allows you to fake web requests. Simple define the URL you're going to call and prepare the response(s). Makes your life very easy.
It looks like you are stubbing out the DocList#authsub_url method in two places :-
The first stub is on any instance of DocList and returns a URL :-
GData::Client::DocList.any_instance.stubs(:authsub_url).returns("http://test.google.com/contacts")
The second stub is on the actual instance of DocList but this returns nil because no there is no returns clause :-
controller.instance_variable_get(:#client).expects(:authsub_url).with(import_method_gmail_url, false, true).once
I think you can achieve what you want by combining them something like this :-
controller.instance_variable_get(:#client).expects(:authsub_url).with(import_method_gmail_url, false, true).returns("http://test.google.com/contacts")
Note that once is the default so is not needed unless you want to emphasise that the method should only be called once.
I am trying to test my update action in Rails with this:
context "on PUT to :update" do
setup do
#countdown = Factory(:countdown)
#new_countdown = Factory.stub(:countdown)
put :update, :id => #countdown.id, :name => #new_countdown.name, :end => #new_countdown.end
end
should_respond_with :redirect
should_redirect_to("the countdowns view") { countdown_url(assigns(:countdown)) }
should_assign_to :countdown
should_set_the_flash_to /updated/i
should "save :countdown with new attributes" do
#countdown = Countdown.find(#countdown.id)
assert_equal #new_countdown.name, #countdown.name
assert_equal 0, (#new_countdown.end - #countdown.end).to_i
end
end
When I actually go through the updating process using the scaffold that was built it updates the record fine, but the tests give me this error:
1) Failure:
test: on PUT to :update should save :countdown with new attributes. (CountdownsControllerTest)
[/test/functional/countdowns_controller_test.rb:86:in `__bind_1276353837_121269'
/Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
/Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: on PUT to :update should save :countdown with new attributes. ']:
<"Countdown 8"> expected but was
<"Countdown 7">.
I'd have thought those end columns would screw up ruby, but looks like it doesn't maybe...?
Anyways, So I assume that /test/functional/countdowns_controller_test.rb:86 is this assertion: assert_equal #new_countdown.name, #countdown.name.
So your assertion is asking that #new_countdown.name and #countdown.name are the same? Open the factory and see, i guess is the easy answer. not sure what you are trying to test here.
also, why is #countdown = Countdown.find(#countdown.id) using the same instance variable name? Isn't #countdown in setup the same as #countdown in your find line in the test?
I'm trying to figure out an inconsistency between what's happening in a functional test and what is happening in my development environment. I have a custom validation method unique_entry that is essentially a specialized version of validates_uniqueness_of. It looks like this:
def unique_entry
matched_entry = Entry.first(:conditions => ['LOWER(field_one) = LOWER(?) AND LOWER(field_two) = LOWER(?)', self.field_one, self.field_two])
errors.add_to_base('Duplicate detected') if matched_entry && (matched_entry.id != self.id)
end
The update action in the controller is very basic:
def update
if #entry.update_attributes(params[:entry])
flash.now[:success] = 'Success'
render :action => 'show'
else
flash.now[:error] = 'Error'
render :action => 'edit'
end
end
This works just fine when I'm creating a new record. When I update a record, however, I get inconsistent behavior. If I test it from a browser in my development environment, it correctly renders the edit action with an error message, but in my functional test, it accepts the update as successful. Here is the test:
test "should not update entry and should render edit view if invalid update" do
put :update, { :id => 1, :field_one => 'new_value', :field_two => 'new_value' } # 'new values' are the same as another existing record to trigger the duplication check
assert_template :edit
assert_not_nil flash[:error]
end
I looked at the test log and discovered that the values unique_entry is using are the record's original values instead of the values it should be attempting to update with. That is, the first line of unique_entry generates an SQL query like this:
SELECT * FROM "entries" WHERE (LOWER(field_one) = LOWER('original_value_of_field_one') AND LOWER(field_two) = LOWER('original_value_of_field_two')) LIMIT 1
What am I missing here? Why do my validations seem to be running against the original record instead of the new values only in the test environment?
In your test, shouldn't there be some reference to :entry, since that is what you are looking for in the controller params[:entry] ?
Hey all, I am completely lost on this one.
I found a code snippet online to help validate fields via ajax as the user types into them. So I'm trying to write a spec against part of it and I just can't get it to pass.
Here's the code
def validate
field = params[:field]
user = User.new(field => params[:value])
output = ""
user.valid?
if user.errors[field] != nil
if user.errors[field].class == String
output = "#{field.titleize} #{user.errors[field]}"
else
output = "#{field.titleize} #{user.errors[field].to_sentence}"
end
end
render :text => output
end
and here is my test so far
describe "POST validate" do
it "retrieves the user based on the past in username" do
mock_errors ||= mock("errors")
mock_errors.stub!(:[]).and_return(nil)
User.should_receive(:new).with({'username'=>"UserName"}).and_return(mock_user)
mock_user.should_receive(:valid?).and_return(true)
mock_errors.should_receive(:[]).with("username").and_return(nil)
put :validate, :field=>'username', :value=>'UserName'
response.should == ""
end
end
I get this error -
1) Spec::Mocks::MockExpectationError
in 'UsersController POST validate
retrieves the user based on the past
in username' Mock 'errors' received
unexpected message :[] with
("username")
I can't seem to figure out how in the world to mock the call to user.errors[field]. Ideally this spec tests the happy path, no errors. I'll then write another for a validation failure.
I'm not seeing mock_user. Here's a shot at it:
describe "POST validate" do
it "retrieves the user based on the past in username" do
mock_errors = mock("errors")
mock_user = mock("user")
mock_user.stub!(:errors).and_return([mock_errors])
mock_errors.stub!(:[]).and_return(nil)
User.should_receive(:new).with({'username'=>"UserName"}).and_return(mock_user)
mock_user.should_receive(:valid?).and_return(true)
mock_errors.should_receive(:[]).with("username").and_return(ActiveRecord::Errors.new({}))
put :validate, :field=>'username', :value=>'UserName'
response.should == ""
end
end
The key is that you need your User mock to respond to the errors method by returning either an empty hash or a hash of fieldname/errors. An alternative to this is to use one of the fixture replacement tools. I'm using machinist right now, which might reduce this whole thing to:
describe "POST validate" do
it "retrieves the user based on the past in username" do
#user = User.make{'username'=>"UserName"}
#user.should_receive(:valid?).and_return(true)
#user.errors.should_receive(:[]).with("username").and_return(ActiveRecord::Errors.new({}))
put :validate, :field=>'username', :value=>'UserName'
response.should == ""
end
end