My app's view specs started failing because of a change we introduced to version control and I'm trying to debug.
Essentially, we had a ton of view specs that always passed, and now many, but not all, of them produce ActionController::UnknownFormat errors in the controller. Here is an example of a method that is blowing up:
def show
#user = current_user
FooCountItem.increment_requests
respond_to do |format|
format.html
format.json { render json: #foo }
end
end
Here's an example of spec that just began to fail:
describe "show.html.erb", type: :feature do
context "as admin" do
let!(:foo) { FactoryGirl.create(:foo) }
let!(:user) { FactoryGirl.create(:admin_user) }
before :each do
page.set_rack_session(user_id: user.id)
visit data_service_path(foo.id)
end
it "displays the foo's name" do
expect(page).to have_content(foo.name)
end
end
end
How is it possible that html is not the expected response? How could the controller react as if it has to produce a different format?
Thanks.
Updated: I'm also seeing NoMethodError: undefined method 'symbolize_keys' for "":String on some lines that are doing the controller requests from the controller_specs. I have no changes in spec_helper.rb when compared to working version of the test suite.
If I add format: :html to a call in the controller spec, it passes. I must have the app globally configured to respond to :js.
Related
rspec/spec/controllers/javascript_controller_spec.rb
describe '#show' do
context 'when the javascript can be found' do
subject { get :show, params: { id: javascript.id } }
it { is_expected.to have_http_status(:ok) }
it 'returns the correct body' do
expect(subject.body).to eq(javascript.to_json)
end
end
context 'When the javascript can\'t be found' do
subject { get :show, params: { id: 'blahdeblah' } }
it { is_expected.to have_http_status(:not_found)}
it 'returns an error' do
expect(subject.body).to eq("{\"code\":404,\"message\":\"Javascript with id 'blahdeblah' not found\"}")
end
end
end
controllers/javascript_controller
class JavascriptController < ApplicationController
# ...
def show
javascript = Javascript.find(params[:id])
javascript_hash = Rails.cache.fetch(javascript.cache_key) {javascript.as_json }
render json: javascript_hash, status: :ok
end
# ...
end
So my first two test are passing but my last two aren't.
I'm getting the error 1) JavascriptController#show When the javascript
can't be found returns an error
Failure/Error: javascript = Javascript.find(params[:id])
ActiveRecord::RecordNotFound:
Couldn't find Javascript with 'id'=blahdeblah
its throwing two of the same errors actually for the last two. I'm trying to test for if the ID is not in the DB then throw an error for not found. Can anyone help me with this issue?
You're writing a controller spec, which is a relatively low-level spec: it will invoke your controller action, but any exception raised from there (like ActiveRecord::RecordNotFound) will bubble straight up into your spec.
To see a more comprehensive view of what will happen with real requests, use a request spec instead. That will pass its request through the full stack, and the exception will then be turned into a 404 response of some sort, as it will in production.
Sorry to say this but RTFM. .find throws RecordNotFound if the "id" cant be found. Your controller is raising an error before it gets to the render line.
In short, the test isn't broken. Your controller doesn't conform to the tests that are written in the test suite so you need to update your controller code so that the test will pass. This is basic Test Driven Development (TDD). Update your controller code so all the tests pass.
I have simple controller:
class CreditLogsController < BaseController
def show
#account = Account.find_by_email(Base64.urlsafe_decode64(params[:id]))
end
end
and here is the spec for it:
require 'rails_helper'
describe CreditLogsController, type: :controller do
describe 'GET #show' do
it 'responds with 200' do
create(:account)
get :show, params: {id: Base64.urlsafe_encode64('tes1#test.com')}, format: :html
puts "############# #{controller.instance_variable_get(:account)}"
expect(assigns(:account)).to eql('tes1#test.com')
end
end
end
The problems is that account in spec is always nil, in coverage file code from controller which assigns value to #account is showed as not covered and controller.instance_variable_get(:account) raises an error:
`account' is not allowed as an instance variable name.
I have similar code in other spec and is working ok, so what am I doing wrong?
As the error indicates, that's the wrong name for an instance variable. They must start with #, and so do the symbols and strings describing them.
You must use:
controller.instance_variable_get(:#account)
or
controller.instance_variable_get('#account')
The rest of your test (expect(assigns(:account))...) will not be reached, as instance_variable_get(:account) will raise a NameError exception.
Following instructions from that Devise How-To page I'm trying to rebuild all my rspec test to bypass user signin process.
There are 2 methods that can be used for that:
sign_in from Devise - which can't be used with feature tests (Capybara)
login_as from Warden (Devise is build on top of it)
1st method at the first shot worked (all test passes) except those with Capybara so I decided to leave it for now.
2nd gives me some weird results - all passes except the first one (any which I place as the first in the file). It fails when I run just one of them. I've checked it with binding.pry and it fails as the user is not logged in and it redirects to the login page. Somehow first test triggers something that makes all the rest passing. I have no clue what's going on here.
I was using around hook before but it behaves really weird so I've changed that to set of before and after (at the same time it works much faster as it creates just one user at the begging rather than for every test). This is how it looks like now:
require 'spec_helper'
describe AlbumsController do
let(:album) { create(:album) }
before(:all) do
#user = create :user
end
before(:each) do
login_and_switch_schema #user
end
after(:all) do
destroy_users_schema #user
destroy_user #user
end
describe "GET #new" do
before { get :new }
it { expect(response).to render_template :new }
end
describe "GET #edit" do
before { get :edit, id: album }
it { expect(response).to render_template :edit }
end
...
and I've defined that helpers:
Warden.test_mode!
def login_and_switch_schema(user)
##request.env["devise.mapping"] = Devise.mappings[:user]
#sign_in :user, user
login_as(user, scope: :user)
Apartment::Database.switch(user.username)
end
def destroy_users_schema(user)
Apartment::Database.drop(user.username)
Apartment::Database.reset
end
def destroy_user(user)
User.destroy(user)
end
I would like to ask you for help.
I would try moving your before(:all) and after(:all) code into the before(:each) and after(:each). :all doesn't play well with let, DatabaseCleaner or in giving you a predictable order of execution for the first test executed.
This following Controller test is failing, and I can't figure out why:
describe "GET 'index'" do
before(:each) do
#outings = FactoryGirl.create_list(:outing, 30)
#user = FactoryGirl.create(:user)
end
it "should be successful" do
get :index
response.should be_success
end
end
Rspec offers up the (rather unhelpful) error:
Failure/Error: response.should be_success
expected success? to return true, got false
Here's the code for the actual Controller, too:
def index
if #user
#outings = Outing.where(:user_id => #user.id)
#outing_invites = OutingGuest.where(:user_id => #user.id)
else
flash[:warning] = "You must log in to view your Outings!"
redirect_to root_path
end
end
Anyone have an idea what's causing my test to fail? I assume it may have something to do with the conditional in the Outing Controller, but I have no idea what a passing test would look like...
You're confusing instance variables between two separate classes - the controller is its own class and the specification is its own class. They don't share state. You could try this simple example to get a better understanding...
def index
// obvious bad code, but used to prove a point
#user = User.first
if #user
#outings = Outing.where(:user_id => #user.id)
#outing_invites = OutingGuest.where(:user_id => #user.id)
else
flash[:warning] = "You must log in to view your Outings!"
redirect_to root_path
end
end
I'll guess that FactoryGirl.create_list(:outing, 30) doesn't create an outing associating the first user with the outing since you're creating the user after you create the outing so your Outing.where will fail as well.
Its important to understand that when you are including the database in your test stack the database needs to contain the data in the way the test expects. So if your controller is querying for outings belonging to a specific user your spec needs to setup the environment such that the user the controller will retrieve (in this case, the terrible line with User.first from my example) will also have the outings associated with it that the specification is expecting.
I want to test that my controller action is rendering a partial.
I've poked around and I can't seem to find anything that works.
create action:
def create
#project = Project.new...
respond_to do |format|
if #project.save
format.js { render :partial => "projects/form" }
end
end
end
spec:
it "should save and render partial" do
....
#I expected/hoped this would work
response.should render_partial("projects/form")
#or even hopefully
response.should render_template("projects/form")
#no dice
end
If you're looking for a REAL answer... (i.e. entirely in RSpec and not using Capybara), the RSpec documentation says that render_template is a wrapper on assert_template. assert_template (according to the docs) also indicates that you can check that a partial was rendered by including the :partial key.
Give this a go...
it { should render_template(:partial => '_partialname') }
Update see bluefish's answer below, it seems to be the correct answer
Would you consider using Capybara for your integration testing? I found ajax difficult to test with rspec alone. In your case I'm not even sure you are getting a response back yet. In capybara it waits for the ajax call to finish and you can call the page.has_xxxx to see if it was updated. Here is an example:
it "should flash a successful message" do
visit edit_gallery_path(#gallery)
fill_in "gallery_name", :with => "testvalue"
click_button("Update")
page.has_selector?("div#flash", :text => "Gallery updated.")
page.has_content?("Gallery updated")
click_link "Sign out"
end
another great way to test your ajax controller method is to check the assignments which are later used to render the result. Here is a little example:
Controller
def do_something
#awesome_result = Awesomeness.generete(params)
end
JBuilder
json.(#awesome_result, :foo, :bar)
Rspec Controller Test
describe :do_something do
before do
#valid_params{"foo" => "bar"}
end
it "should assign awesome result" do
xhr :post, :do_something, #valid_params
assigns['awesome_result'].should_not be_nil
end
end