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.
Related
I created a destroy method and now I wanted to know how I can test and render if the object manages to be removed or not.
def destroy
if #syllabus.destroy
render :no_content
else
end
end
I think you are looking for something like rspec-rails,
after following the installation instructions on the gem repository you can generate a testing file with:
bundle exec rails generate rspec:controller my_controller
this would generate a file like the following:
# spec/controllers/my_controller_spec.rb
require 'rails_helper'
RSpec.describe MyControllerController, type: :controller do
# your code goes here...
end
then you can add a test example like this one:
# spec/controllers/my_controller_spec.rb
require 'rails_helper'
RSpec.describe MyControllerController, type: :controller do
#replace attr1 and attr2 with your own attributes
let(:syllabus) { Syllabus.create(attr1: 'foo', attr2: 'bar') }
it 'removes syllabus from table' do
expect { delete :destroy, id: syllabus.id }.to change { Syllabus.count }.by(-1)
end
end
** the code above is not test and it was made just as a guide **
for you destroy action method it's ok but, you could improve it a bit if you leave it like:
def destroy
#syllabus.destroy
end
this is because your if/else condition is not doing much on the method and rails by default should respond with a 204 no content
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
I am looking for clarification and an understanding on how to effectively test my controllers with Rspec, I don't want to write tests that are not testing the potential issues at hand.
My scenario is as follows.
I am using Active Admin to create a Category, to do so you must obviously be logged into Active Admin.
What I want to ensure is that
1) A logged in user can create a Category
2) A Category cannot be created if you are not logged in
3) Attempts to create a Category outside of active admin are met with a 404 template
So what i have so far (and i really want to check i haven't gone over the top or performing unnecessary tests) is as follows.
spec/controllers/categories_controller_spec.rb
require 'rails_helper'
include Warden::Test::Helpers
# Ensure 404 pages are returned when requesting URLS
RSpec.describe CategoriesController, type: :request do
describe 'Routes' do
context 'All CRUD actions render 404' do
it '#create' do
post '/categories'
expect(response.status).to eq(404)
expect(response).to render_template(:file => "#{Rails.root}/public/404.html.erb")
end
# All other actions here
end
end
end
RSpec.describe Admin::CategoriesController, type: :request do
describe 'No Authorised Login' do
context 'All CRUD actions redirect correctly' do
it 'redirects when accessing #index' do
get '/admin/categories'
expect(response.status).to eq(302)
expect(response).to redirect_to(admin_root_path + '/login')
end
# All other actions here
end
end
end
# Ensure actions in admin can be carried out if logged in
RSpec.describe Admin::CategoriesController, type: :request do
before(:each) do
#user = FactoryGirl.create(:admin_user)
login_as #user
end
after(:each) do
#user.destroy
end
describe 'Authorised Login' do
context 'All CRUD actions perform as expected' do
it 'navigates to Categories #index' do
get '/my_admin_panel/categories'
expect(response.status).to eq(200)
expect(response).to render_template(:index)
end
# All other actions here
end
end
spec/routing/categories_routing.spec
RSpec.describe CategoriesController, type: :routing do
describe 'Routes' do
it 'does not get #index' do
expect(get: '/categories').to route_to(
controller: 'application',
action: 'raise_not_found',
unmatched_route: 'categories'
)
end
end
end
Should I be testing post /categories without supplying params, is that a wasted test? Am I over complicating what should be a simple set of tests ?
This is a judgement/style question and, as such, is not ideal for the StackOverflow format. That said, I don't think your testing is over the top. Some other thoughts:
Some people choose treat their controller tests as integration tests.
You can take advantage of RSpec's shared examples to DRY up your tests
In the default RSpec configuration, the database will be cleaned after each test, so you don't need to explicitly destroy the ActiveRecord objects you create if it's the database you're worried about
In general, I think testing behavior for programmatic actions you don't expect to happen (e.g. posting to undefined routes) is worthwhile unless you are trying to test specific error handling code
I have a helper method that uses 'request' to determine the URL. However, rspec can't seem to find request. I thought request was available to all front-facing tests?
How can I account for the request method in my spec?
Helper Spec
require 'spec_helper'
describe ApplicationHelper do
describe "full_title" do
it "should include the page title" do
expect(full_title("help")).to include('help')
end
end
end
Helper methods
def full_title(page_title)
if staging? # causing the issue
base_title = "Staging"
else
base_title = "Company Name"
end
if page_title.empty?
"#{base_title} | Tag line "
else
"#{base_title} | #{page_title} "
end
end
def staging? # the request here seems to be the problem
request.original_url.include? "staging"
end
Rspec error
Failure/Error: expect(full_title("help")).to include('help')
NameError:
undefined local variable or method `request' for #<RSpec::ExampleGroups::ApplicationHelper_2::FullTitle:0x00000106260078>
Thanks in advance.
First off: request is only available in the controller tests (and even then only in the request specs I think), helper tests are really basic and isolated. Which is good. Your helper code should be really minimal and normally only work on the input it receives.
However this is pretty easily solvable by using stubbing.
So write something like
#note, OP needed to replace 'helper' with 'self'for Rails 4.0.0 and Rspec 3.0
require 'rails_helper'
describe ApplicationHelper do
describe "full_title" do
context "in staging" do
it "should include the page title" do
helper.should_receive(:staging?).and_return(true)
expect(full_title("help")).to include('help')
end
end
context "not in staging" do
it "should include the page title" do
helper.should_receive(:staging?).and_return(false)
expect(full_title("help")).to include('help')
end
end
end
end
Which is imho a very clear, and then you write separate tests for your staging? method:
describe "staging?" do
context "when in staging" do
it "returns true" do
helper.stub(:request) { OpenStruct.new(original_url: 'staging') }
expect( helper.staging? ).to be true
end
end
context "when not in staging" do
it "returns false" do
helper.stub(:request) { OpenStruct.new(original_url: 'development') }
expect(helper.staging?).to be false
end
end
end
end
Some small remarks: ruby default indentation is 2 spaces.
Secondly, your function now literally says return true if true, ideally it should be written like
def staging?
request.original_url.include? "staging"
end
i am using grape for creating rest api i created the api and its working fine now i have to test this api.when we create rails api there is automatically spec_helper.rb file is generated now as usual first line for testing is
require spec_helper
please tell what should be the code for spec_helper.rb file
and other things i should focus when testing a simple rake application.i am giving a small code snippet for example i have to test
require 'grape'
require 'sequel'
require 'json'
module Twitter
class API < Grape::API
version 'v1', :using => :header, :vendor => 'twitter'
format :json
helpers do
def current_user
#current_user ||= User.authorize!(env)
end
def authenticate!
error!('401 Unauthorized', 401) unless current_user
end
end
resource :users do
desc "Return a status."
params do
requires :id, :type => Integer, :desc => "Status id."
optional :include , :type => String , :desc =>"parameter to include in "
end
get ':id' do
"Hello World"
end
in this grape application when i call localhost:9292/users/1234
then response should be "Hello World" how to test this app what should be content of spec_helper.rb file for testing.i am using only grape not using rails
It all depends on what you want to test.
Presumably the route you want to test (localhost:9292/users/1234) is a UsersController. That being the case, you will want to do something like this (using rspec):
describe UsersController do
context "GET#show" do
it "should return 'Hello World'" do
get :show, id: 1234
response.body.should include 'Hello World'
end
end
end
Now as for the rake task test, I would create an integration test that executes the rake task from the command-line and compares expected results to the output of the rake task sort of like this:
describe "My Rake Task" do
it "should return hello world" do
results = `bundle exec rake my:rake:task`
results.should include 'Hello World'
end
end
Hope these rough examples work for you! Good luck!
UPDATE:
You should always write unit test on classes as much as possible so your rake task tests are very simple or not even needed.
I think you mean Rack app. There's a pretty decent section on testing in the README for Grape. You should start there.
https://github.com/intridea/grape#writing-tests