I need to be able to test the contents of a CSV file that my Rails application is returning.
In my controller, the code looks like:
respond_to do |format|
format.html
format.js
format.csv do
if current_user.has_rights?
response.headers['Content-Type'] = 'text/csv'
response.headers['Content-Disposition'] = 'attachment; filename=info.csv'
send_data generate_csv_file
else
send_data "Access denied"
end
end
end
And this code works-- if I visit that URL with the appropriate rights, then the CSV file is downloaded. However, I can't seem to get any kind of appropriate test working with Poltergeist and Capybara.
If I do the following, following the response to this question:
describe DashboardsController do
context "when format is csv" do
render_views
let(:csv_string) { get_csv_headers }
let(:csv_options) { {filename: "report.csv", disposition: 'attachment', type: 'text/csv; charset=utf-8; header=present'} }
let (:csv_user) {FactoryGirl.create(:csv_user)}
before do
sign_in csv_user
end
it "should return a csv attachment" do
# #controller.should_receive(:send_data).with("#{csv_string.join(',')}", csv_options).
# and_return { #controller.render nothing: true } # to prevent a 'missing template' error
get :index, format: :csv
puts response.headers
puts response.body
end
end
end
The header that's reported via that puts:
{"Location"=>"http://test.host/", "Content-Type"=>"text/html; charset=utf-8"}
<html><body>You are being redirected.</body></html>
which is clearly wrong. What can I do to get the response for a csv format be csv within the context of the test? (please note that I've already included render_views, as suggested in this question).
I'm suffering through the same issue right now. This might help you. => http://sponsorpay.github.io/blog/2012/11/29/capybara-poltergeist-and-csv-downloads/
This may also be relevant. => Downloading file to specific folder using Capybara and Poltergeist driver
Related
I've got an action in my rails app that responds both in html and js format.
The result, according to the format, changes a bit. So I'd like to write one test for html and other for js response.
I'm using rspec.
Controller:
# app/controllers/my_controller.rb
class MyController < ApplicationController
def my_action
respond_to do |format|
format.html do
# Do something...
end
format.js do
# Do something else...
end
end
end
end
Spec:
# spec/requests/my_spec.rb
require "rails_helper"
RSpec.describe "My", type: :request do
describe "GET /my_action" do
context 'HTML' do
# HTML tests
end
context 'JS' do
# JS tests
end
end
end
Rails detects request format via http Accept header, in tests there's a helper for simulating xhr (also sets X-Requested-With, which is part of CSRF mitigation):
get "/some/path/to/my_action", xhr: true
Hi I tried to add test to my controller I have a method that download a excel file
In my method controller i have this code.
def download_syllabus_evaluation_system
#syllabus_programs = SyllabusProgram.match_course_coordinator.actives.order(:id)
name_file = "Syllabus_evalution_system"
setHeaderExportExcel(name_file)
respond_to do |format|
format.excel { render template: '/syllabuses/excel/syllabus_evaluation_system', layout: 'report/excel/template', status: :ok}
end
end
And this method that set Header
def setHeaderExportExcel(name_file)
headers['Content-Type'] = "application/vnd.ms-excel; charset=UTF-8; charset=ISO-8859-1; header=present"
headers['Content-Disposition'] = 'attachment; filename="' + name_file +'.xls"'
headers['Cache-Control'] = ''
end
In My Rspec Controller I have this code
require 'rails_helper'
#TODO: add test controller
RSpec.describe SyllabusesController, type: :controller do
describe "syllabus_program#update " do
context "as an authorized " do
before do
##user = FactoryBot.create(:user)
end
end
end
describe "GET#download_syllabus_excel" do
it "return succes response" do
get download_syllabus_evaluation_system_path(format: "excel")
expect(response).to be_successful
end
end
end
And my route is
get 'download_syllabus_evaluation_system' => 'syllabuses#download_syllabus_evaluation_system', as: "download_syllabus_evaluation_system"
But when i run the test , i get this error
Failure/Error: get download_syllabus_evaluation_system_path(format: "excel")
ActionController::UrlGenerationError:
No route matches {:action=>"/download_syllabus_evaluation_system.excel", :controller=>"syllabuses"}
Whats the problem?
In my Rails 5 app I have this:
class InvoicesController < ApplicationController
def index
#invoices = current_account.invoices
respond_to do |format|
format.csv do
invoices_file(:csv)
end
format.xml do
invoices_file(:xml)
end
end
end
private
def invoices_file(type)
headers['Content-Disposition'] = "inline; filename=\"invoices.#{type.to_s}\""
end
end
describe InvoicesController, :type => :controller do
it "renders a csv attachment" do
get :index, :params => {:format => :csv}
expect(response.headers["Content-Type"]).to eq("text/csv; charset=utf-8")
expect(response).to have_http_status(200)
expect(response).to render_template :index
end
end
My problem is that my Spec always passes (!), even when I put a bunch of crap into my index.csv.erb file. It seems that the view file isn't even evaluated / tested by RSpec.
How is this possible? What am I missing here?
Controller tests/specs are these weird stubbed creations born out of the idea of unit testing controllers in isolation. That idea turned out to be pretty flawed and has really fallen out of vogue lately.
Controller specs don't actually make a real HTTP request to your application that passes through the routes. Rather they just kind of fake it and pass a fake request through.
To make the tests faster they also don't really render the views either. Thats why it does not error out as you have expected. And the response is not really a real rack response object either.
You can make RSpec render the views with render_views.
describe InvoicesController, :type => :controller do
render_views
it "renders a csv attachment" do
get :index, format: :csv
expect(response.headers["Content-Type"]).to eq("text/csv; charset=utf-8")
expect(response).to have_http_status(200)
expect(response).to render_template :index
end
end
But a better and more future proof option is using a request spec.
The official recommendation of the Rails team and the RSpec core team
is to write request specs instead. Request specs allow you to focus on
a single controller action, but unlike controller tests involve the
router, the middleware stack, and both rack requests and responses.
This adds realism to the test that you are writing, and helps avoid
many of the issues that are common in controller specs.
http://rspec.info/blog/2016/07/rspec-3-5-has-been-released/
# spec/requests/invoices
require 'rails_helper'
require 'csv'
RSpec.describe "Invoices", type: :request do
let(:csv) { response.body.parse_csv }
# Group by the route
describe "GET /invoices" do
it "renders a csv attachment" do
get invoices_path, format: :csv
expect(response.headers["Content-Type"]).to eq("text/csv; charset=utf-8")
expect(response).to have_http_status(200)
expect(csv).to eq ["foo", "bar"] # just an example
end
end
end
The format option should be specified outside of the params, i.e. get :index, params: {}, format: :csv}.
Regarding RSpec evaluating views, no, in controller tests, it doesn't, regardless of the format. However, it's possible to test views with RSpec: https://relishapp.com/rspec/rspec-rails/v/2-0/docs/view-specs/view-spec
There is the following code:
describe 'Some title' do
before do
session = ActionController::TestSession.new
session[:state] = "12334"
get '/api/v1/menus', format: :json
end
it 'some text' do
expect(response).to be_success
json = JSON.parse(response.body)
puts json
end
end
Code of the controller:
class Api::V1::MenusController < Api::V1::ApiV1Controller
def index
render json: session
end
end
But controller returns an empty session always. How can I fix it?
Try adding this:
describe 'Some title', :type => :controller do
And remove session = ActionController::TestSession.new.
RSpec needs to know you are doing "controller things" in your test. You indicate this as above or by placing the test in spec/controllers.
I want to make sure my CSV download contain the correct columns. When I test a CSV download with RSpec I cannot access the file contents. How do I access the contents of the CSV file?
require 'spec_helper'
include Devise::TestHelpers
describe Admin::ApplicationsController do
before(:each) do
#application = FactoryGirl.create :application
#user = FactoryGirl.create( :admin_user )
sign_in #user
end
it "downloads a csv"
it "gives us only the columns we want" do
get :index, format: :csv
p response.body
p response.headers
end
end
The output of the test:
# This is the output in the terminal
# ""
# {"Content-Type"=>"text/csv; charset=utf-8", "Content-Disposition"=>"attachment; filename=\"applications-2013-12-17.csv\""}
At your describe block call render_views as in:
describe Admin::ApplicationsController do
render_views
... # all the other code
end
Calling render_views instructs RSpec to render the view contents inside a controller spec. This is turned off by default because when you're running controller specs you usually don't care about the view contents and this makes your tests run faster.
You can see the official documentation for the latest Rails version here.