Is it possible to examine if get request rendered text?
I know there are hacks like response.body == 'any string' but it does not interest me. I'm just wondering if there is "RSpec™" way to do it.
Having this rspec:
RSpec.describe MyController, type: :controller do
controller do
def index
render text: 'Hellow'
end
end
describe 'rendering' do
subject { get :index }
it { is_expected.to render_template(text: 'Hellow')}
end
end
I would love to be able to call it { is_expected.to render_template(text: 'Hellow')}. It raises:
Failure/Error: it { is_expected.to render_template(text: 'Hellow') }
ArgumentError:
Unknown key: :text. Valid keys are: :layout, :partial, :locals, :count, :file
or maybe it { is_expected.to render_template('Hellow')}
Failure/Error: it { is_expected.to render_template('Hellow') }
expecting <"Hellow"> but rendering with <[]>
Is there any RSpec™ way to accomplish it?
Testing expect(response.body).to eq('Hellow') is totally appropriate.
The reason is_expected.to render_template isn't working is you aren't rendering a template. If your controller omitted an explicit render call, Rails would render the index template for you, and you could test render_template(:index). You could also render template: :foo and then test render_template(:foo) if you wanted to render a nonstandard template. But when you render text: 'Hellow', you aren't using templates; you're explicitly setting the response body to the text you specify.
If you do render a template, and you want to test the content rendered by that template, that's when render_views comes into play, as gotva mentioned. Even then, you'd be checking for content in response.body, as you can see in RSpec's own examples. As your templates get complicated, the controller specs aren't the appropriate place for this and you should start writing view specs using assert_select or something similar.
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 a rails controller that returns only json
def index
if params[:filtered] = 'someValue'
#json = Model.where(some_conditions).to_json
else
#json = Model.where(some_other_conditions).to_json
end
render json: #json
end
What is the correct way to test that the action is returning the expected #json objects?
I've tried the following
describe "GET #index" do
before :each do
get :index, filtered: 'someValue'
end
it { expect( response.body ).to eq 'my expected response' }
end
But I'm getting
Failure/Error: it { expect( response.body ).to eq 'my expected response' }
expected: 'my expected response'
got: "[]"
I'm having trouble determining whether there is a problem in the underlying controller, or whether I've simply written a bad test.
Is response.body the correct way to get the json payload?
Help appreciated!
Both your controller and spec are somewhat off.
You don't need to call to_json on the object that you want to render.
If you use the :json option, render will automatically call to_json
for you.
http://guides.rubyonrails.org/layouts_and_rendering.html
The reason your spec is giving you "[]" is that Model.where(some_conditions) is returning an empty collection. An empty collection renders as an empty array in JSON.
Either the scope does not work as intended or your test setup is flawed. Remember that let variables are lazy loading and you either need to use let! or reference the variable for records to be inserted into the test db.
# polyfill for Rails 4. Remove if you are using Rails 5.
let(:parsed_response) { response.body.to_json }
describe "GET #index" do
# or use fixtures / factories
let!(:model) { Model.create!(foo: 'bar') }
before :each do
get :index, filtered: 'someValue'
end
expect(parsed_response.first["id"].to_i).to eq model.id
end
I try to add RSpec test for invalid entry create controller action, which responds to HTTP POST verb.
it "case of invalid entry" do
program=FactoryGirl.attributes_for(:program, :faculty_id => faculty.id, :code=>"воруй&убивай")
post :create, {:program=>program, :faculty_shortcut=>faculty }
expect(response).to render_template(:new)
end
Controller:
if #program.save
redirect_to faculty_program_path(faculty, #program)
else
render :new,:json => #program.errors
end
What I get from RSpec
Failure/Error: expect(response).to render_template(:new)
expecting <"new"> but rendering with <[]>
This means that the record has been saved and you have been redirected. If you want to check behaviour when invalid data is passed, do not rely on existing validations as they might change in the future. Just stub valid? method:
it "case of invalid entry" do
program=FactoryGirl.attributes_for(:program)
allow_any_instance_of(Program).to receive(:valid?).and_return false
post :create, {:program=>program, :faculty_shortcut=>faculty }
expect(response).to render_template(:new)
end
Stubbing valid? has this advantage that your test will not change when you change your validations. Validations on their own should be tested within model tests, this way single change in the code won't cause multiple test fails.
I'm trying to do some model_spec testing but having trouble with not having to further nest my rspec code. It would be great if in this case, I could just have a set of "it's" instead of having to add context everytime I want to switch the variable var. Here's the following code:
describe "#some_method" do
subject { course.some_method(var) }
context 'given a project' do
let(:var) {random[1]}
it 'returns the one after' do
is_expected.to eq(random[2])
end
context 'being the last' do
let(:vars) {random.last}
it 'returns nil' do
is_expected.to be_nil
end
end
context '...you get the point, being something else' do
let(:vars) { something.else }
it 'returns nil' do
is_expected.to.to be_nil
end
end
end
end
Maybe I'm just stuck in the wrong mode of thinking and someone could think of a better way for me to do this? I've been suggested that I absolutely must use the subject by someone I work for.
At first, I disagreed and thought it was getting a little burdensome but then I figured keeping subject and having let(:var) apply to it was pretty useful...
RSpecs subject is a tool which can be used to make tests more succinct. There are many cases where it makes sense to use the subject:
RSpec.describe User do
# with the help of shoulda-matchers
it { should validate_uniqueness_of :username } # implicit subject
end
RSpec.describe UsersController do
describe '#show' do
it 'is successful' do
get :show
expect(response).to have_http_status :success
end
it 'renders template show' do
get :show
expect(response).to render_template :show
end
end
#vs
describe '#show' do
subject { response }
before { get :show }
it { should have_http_status :success }
it { should render_template :success }
end
end
And there are cases where using subject will hurt the readability and acuity of your tests.
Your college is just plain wrong in insisting that you always use subject.
A good rule of hand is that if you need an it block then you should not be using subject or is_expected.
If you are describing the call signature of a method you should be calling it in your specs in the same way you would in real life.
let(:decorator){ described_class.new(user) }
describe "#link" do
it 'takes a class option' do
expect(decorator.link(class: 'button')).to match /class=\"button/
end
end
I would recommend running rspec with the --format documentation option and checking if the output actually makes sense. This can be quite important once you get 100s of specs as it gets harder to remember what a behavior a spec actually covers.
How about you write it like this?
expect(subject.call(foo)) is not very pretty but it gets rid of the nesting.
describe "#some_method" do
subject { course.method(:some_method) }
it 'returns the one after if given a project' do
expect(subject.call(random[1])).to eq(random[2])
end
it 'returns nil when it is the last' do
expect(subject.call(random.last)).to be_nil
end
it 'returns nil...' do
expect(subject.call(something.else)).to be_nil
end
end
I have a rails 3.2.13 app running rspec-rails 2.14.0 and am trying to confirm that a view renders a particular partial in my test. It actually does work, but I need to add this test. Here's what I have so far:
require 'spec_helper'
describe 'users/items/index.html.haml' do
let(:current_user) { mock_model(User) }
context 'when there are no items for this user' do
items = nil
it 'should render empty inventory partial' do
response.should render_template(:partial => "_empty_inventory")
end
end
end
This runs without error, but does not pass. The failure is:
Failure/Error: response.should render_template(:partial => "_empty_inventory")
expecting partial <"_empty_inventory"> but action rendered <[]>
Thanks for any ideas.
EDIT
This works for me, but Peter's solution is better...
context 'when there are no items for this user' do
before do
view.stub(current_user: current_user)
items = nil
render
end
it 'should render empty inventory partial' do
view.should render_template(:partial => "_empty_inventory")
end
end
For some reason it was counter-intuitive to me to have to call render on a view, but there you go...
So the way one usually tests whether a particular partial is rendered in a view spec is by testing the actual content of the partial. For example, assume that your _empty_inventory parial has the message 'There is no inventory'. Then you might have a spec like:
it "displays the empty inventory message" do
render
rendered.should_not have_content('There is no inventory')
end
Alternately, you could use a controller spec, in which case you need to call the 'render_views' method when setting up the spec. Then you can do something similar to
it 'should render empty inventory partial' do
get :index, :user_id => user.id
response.should render_template(:partial => "_empty_inventory")
end
Assuming you've set up the state for the contoller spec.