How to stub paperclip file paths in Capybara/Rspec - ruby-on-rails

For some application I am using Paperclip for file upload (actually the dm-paperclip flavour), and Factory Girl, Rspec, Capybara for testing.
I have a very simple Factory for the "Picture" model, where I am stubbing my file properties as suggested in this post:
FactoryGirl.define do
factory :picture do
title "My Picasso"
description "It's like looking in a mirror."
picture_file_file_name { 'spec/resources/img_1.jpg' }
picture_file_content_type { 'image/jpg' }
picture_file_file_size { 1024 }
end
end
In diverse feature tests with Capybara, I visit pages in which the templates feature thumbnails of the Picture instances:
feature "List of Pictures", :js => true do
scenario "displays appropriately the index page of the pictures with pagination" do
FactoryGirl.create_list(:picture, 21)
visit '/pictures'
# And more testing...
end
end
An example of partial used in one of the templates:
= content_tag_for(:li, picture, :class => 'listed_picture') do
= link_to picture_path(picture) do
- if picture.picture_file?
= image_tag picture.picture_file.url(:thumb)
The problem I have now, is whenever I run the specs, the test fails because there is no matching route for the thumbnail url:
No route matches [GET] "/system/picture_files/1/thumb/img_1.jpg"
Is there any way to stub Paperclip's helper methods to make the test pass?
Thanks in advance for any help!

I just went through this process. Here's how I solved the issue.
First, I created a single method on the object to reference the image URL, both to abide by the law of Demeter and to make for an easier test. For you, that might look like:
#picture.rb
class Picture
...
def picture_file_url(size = nil)
picture_file.url(size)
end
...
end
Now we're ready to stub the Paperclip attachment URL in the spec:
describe "List of Pictures", :js => true do
it "displays appropriately the index page of the pictures with pagination" do
let(:picture) { create(:picture) }
allow(Picture).to receive(:picture_file_url) { "url" }
visit '/pictures'
# And more testing...
end
end
Hope this helps you or someone.

Related

Mocking download link / checking link generation against controller methods with Capybara

In a Capybara feature spec for a certain page, I have a download link:
download_link = find_link(expected_link_text)
I want to check that the generated link is the correct one to download the file, i.e., that it will call download() on my FileController with the correct model object.
RSpec-Rails seems to have lots of ways to get what I want. In a controller spec, for instance, I could use an ordinary RSpec assertion on the controller:
expect(controller).to receive(:download).with(expected_id)
# download_link = find_link(expected_link_text) # can't do this in a controller spec
# visit(download_link) # can't do this in a controller spec
In a routing spec, I could use route_to():
# download_link = find_link(expected_link_text) # can't do this in a routing spec
expect(get: download_link[href]).to route_to(controller: 'file', action: 'download', id: expected_id)
But in a feature spec, neither controller nor route_to() is available.
With the following shenanigans and a lot of poking around in the debugger, I was able to get route_to() included in my test:
describe 'the page' do
it 'should let the user download a file' do
self.class.send(:include, RSpec::Rails::Matchers::RoutingMatchers) # hack to get routing matchers into feature test
self.class.send(:include, ActionDispatch::Assertions::RoutingAssertions) # RoutingMatchers uses this internally
self.class.send(:define_method, :message) { |msg, _| msg } # RoutingAssertions expects message() to be included from somewhere
#routes = Rails.application.routes # RoutingAssertions needs #routes
download_link = find_link(expected_link_text)
expect(get: download_link[href]).to route_to(controller: 'file', action: 'download', id: expected_id) # works!
end
end
This actually does work, but it's bananas. Isn't there any out-of-the-box way to mix Capybara into other kinds of specs, or mix other kinds of specs into feature specs? Or just a cleaner Rails-y (maybe non-RSpec) way to get the route?
Note: the route isn't named, so I can't use a URL helper (I don't think); and the URL path itself is incoherent noise for historical reasons, so I don't just want to assert the href in string form.
As you stated, if you want to check a specific controller method is being called that would be a controller spec, if you want to verify the route it would be a routing spec. With Capybara you should be writing feature specs/system tests - that means no mocking/stubbing and instead running end-to-end tests. Configure whatever driver you're using to download files, then click the link, download the file, and verify the correct file was downloaded. The other option is to just use url_for rather than trying to include all the extra stuff and just do
expect(download_link[href]).to eq url_for(controller: 'file', action: 'download', id: expected_id)
or better yet
expect(page).to have_link(expected_link_text, href: url_for(controller: 'file', action: 'download', id: expected_id))
But if you're testing file download, you really should just download the file.
If you have to deal with encoding issues you could rewrite the expectation with a filter block and parse both the paths/urls to normalize
expect(page).to have_link(expected_link_text) do |link|
Addressable::URI.parse(link[:href]) == Addressable::URI.parse(url_for(controller: 'file', action: 'download', id: expected_id))
end

how can I use rspec to test an Ajax request?

I am trying to test an ajax request in rspec, but am not quite sure how to do it at this point It doesnt matter if it is rspec or capybara, I am just trying to get the test to pass, any advice is greatly appreciated
describe "Cart", js: true, search: true do
let(:product) { create(:product) }
let(:variant) { create(:variant, :product => product, :count_on_hand => 1, :sku=>"YIG01276") }
let(:flash_sale) { create(:flash_sale) }
let(:user) { create(:user) }
before do
flash_sale.variants << variant
flash_sale.save
product.reload
end
it "displays expiring time in cart" do
login_user user
visit spree.product_path(product)
click_button 'Add To Cart'
user.last_incomplete_spree_order.expires_in.should > 0
end
end
if you want to check behavior after ajax call, like update view. You need use capybara https://github.com/jnicklas/capybara with driver which support javascript, like selenium, webkit
if you just want to test the request, you can use rack-test directly

Ruby on rails: Cucumber w/Capybara goes to ApplicationController on redirect_to

I'm trying to set up a RESTful API to a database of redirect links. I have set up a lot of tests in cucumber one of which is when a user does a GET on /links/:id. This is supposed to redirect the user to the link. It works in the browser but I'm having some trouble setting up this test in cucumber.
Given /^The link id part of the URL matches an existing entry in the links table$/ do
FactoryGirl.create(:users)
FactoryGirl.create(:links, :OWNER_USERID => Users.first.id)
Users.count.should==1
Links.count.should==1
end
When /^you use GET on link$/ do
visit link_path(Links.first.id)
end
The link_path specified sends me to this show method:
def show
redir=Links.find_by_id(params[:id])
redirect_to redir.target, :status=>307
end
The problem is just that cucumber fails on the When part complaining that I doesn't have a template for application/index. For some reason it does not redirect me but rather goes to root_path of my own site. Anyone knows how to check if such a redirect actually works?
Have you tried testing it in RSpec instead of Cucumber? Try something like this and see if it works:
describe "testing links" do
subject { page }
describe "use GET on link" do
let(:links) { Links.first }
before { get link_path(Links.first.id) }
specify { response.should redirect_to("http://example.com") }
end
end
Also make sure your test database if properly configured and populated before doing the tests.
rake db:migrate and rake db:test:prepare

Rails 2.3.x - How to stub a helper method (that gets called from a view) in a functional test (no RSpec)?

Please don't tell me "search more" or other stuff cause all solutions for similar question fail.
Simple:
I have a functional tests. I want to make a simple get and see if proper content gets rendered
test "displays headline if user should see it" do
get :index
assert_match /headline/, response.body
end
test "doesn't display headline if user shouldn't see it" do
get :index
assert_no_match /headline/, response.body
end
and a simple view
<% if show_headline?(arg) %>
headline
<% end %>
and a helper:
module TheHelper
def show_headline?(arg)
arg ? hard_code_logic : even_harder_logic
end
end
so what I need is to do in test something like:
test "displays headline if user should see it" do
Something.stubs(:show_headline?).returns(true)
get :index
assert_match /headline/, response.body
end
test "doesn't display headline if user shouldn't see it" do
Something.stubs(:show_headline?).returns(false)
get :index
assert_no_match /headline/, response.body
end
The question is what is Something? I want to stub it cause I have helpers tested in unit/helpers.
After the get helper module gets remixed into the controller class. Please don't give me links to other answers, I read them (but of course I could have read the wrong ones) and they don't work for me. I use Rails 2.3.10 with mocha 0.9.8.
Things that don't work:
TheController.any_instance.stubs(:show_headline?)
ActionView::Base.any_instance...
#controller.stubs...
UPDATE:
the only mock that worked was:
<% self.stubs(:show_headline?).returns(true) >%
<% if show_headline?(arg) %>
headline
<% end %>
but of course I will not use that... maybe it is a clue

testing REST with shoulda and factory_girl - destroy

i'm developing test for REST using shoulda and factory_girl. Code below
context "on :delete to :destroy" do
setup do
#controller = NewsArticlesController.new
#request = ActionController::TestRequest.new
#response = ActionController::TestResponse.new
#news_article = Factory.create(:news_article)
end
should "destroy new NewsArticle" do
assert_difference('NewsArticle.count', -1) do
delete :destroy, :id => #news_article.id
end
end
should_redirect_to news_articles_path
end
as a result i see
1) Error:
test: on :delete to :destroy should redirect to index. (NewsArticlesControllerTest):
ArgumentError: block not supplied
c:/develop/ruby/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/action_controller/macros.rb:201:in `instance_eval'
c:/develop/ruby/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/action_controller/macros.rb:201:in `__bind_1248853182_16800
0'
c:/develop/ruby/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
c:/develop/ruby/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: on :delete to :destroy should redirect to index. '
Could you tell me plz - whats wrong and how i can modify test to make them work right?
UPD: routes looks fine
news_articles GET /news(.:format) {:controller=>"news_articles", :action=>"index"}
The problem is with should_redirect_to which now uses block to evaluate the redirect code. Sadly, neither thoughtbot wiki, nor the readme at github reflect this and still contain the old examples.
The correct code is
should_redirect_to "news articles page" { news_articles_path }
where the first argument is just a textual description (it is not eval'd as with the older version) used to generate a test name, so you get a test name like 'should redirect to news articles page'
Maybe you should use a symbol and post method when calling delete:
assert_difference 'Article.count', -1 do
post :delete, :id => ...
end
(referenced from http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#M001427)
tkramar solution points in the right direction, but i've had to write the code as:
should_redirect_to("news articles page") { news_articles_path }
Also see the new manual at http://dev.thoughtbot.com/shoulda/classes/Shoulda/ActionController/Macros.html#M000015

Resources