I have run into this issue a few times, and was wondering what the proper way to deal with it is.
Basically, I am writing a simple capybara feature in rspec:
describe 'Some Feature', type: :feature do
context "when visiting the /some/page/:id" do
it "shows Desired Content" do
visit "/admin/pages/:id"
expect(page).to have_content("Desired Content")
end
end
end
In the view at views/admin/pages/show.html.erb is a call to a helper method located in the corresponding helper (Admin::PagesHelper). In normal rails, these helper module methods are automatically available for use. When i run this feature test though, I get an error stating
ActionView::Template::Error:
undefined method `some_admin_pages_method' for #<#<Class:0x007fe1ace881f0>:0x007fe1ad364d40>
What is the proper approach to getting these helper methods to be available when running my rspec features?
Related
I am writing some tests for a controller tasks, the index action, which has an instance variable #tasks with all the tasks (Task.all).
If I follow the official documentation:
RSpec.describe TeamsController do
describe "GET index" do
it "assigns #teams" do
team = Team.create
get :index
expect(assigns(:teams)).to eq([team])
end
it "renders the index template" do
get :index
expect(response).to render_template("index")
end
end
end
The assigns method is moved to the gem file 'rails-controller-testing'.
I have two questions:
1 - How can I achieve the same as expect(assigns(:teams)).to eq([team]). I guess I am asking, how can I check if I have an instance variable in the index action with values [team]
2 - If this method was moved to the gem, I read in the Github issues, that the reason is: You shouldn't test it there, controller should just test response, cookies etc. But I am confuse, since in relish you can test the instance variable. Should I test it there or not? If not, where? In my views/index_spec.rb, testing if I have all the teams?
3 - Alternative: Since TeamsController is a normal class, should I create a spec in the spec/models/folder spec/models/tasks_controller.rb and there test if the method index has the instance variable #teams with the content that I want?
Thanks
The whole idea is that instead of poking inside your controller and testing its internal variables is flawed you should instead test your controllers by testing the output.
In RSpec you can do this with request and feature specs.
# config/specs/features/teams_spec.html
RSpec.feature 'Teams' do
scenario 'when a user views the teams' do
Team.create(name: 'Team Rocket')
visit '/teams'
expect(page).to have_content 'Team Rocket'
end
end
# config/specs/requests/teams_spec.html
RSpec.describe 'Teams', type: :request do
describe 'GET /teams.json' do
it "includes the team" do
team = Team.create(name: 'Team Rocket')
get teams_path(format: :json)
expect(parsed_response.first['name']).to eq 'Team Rocket'
end
end
describe 'GET /teams' do
it "includes the team" do
team = Team.create(name: 'Team Rocket')
get teams_path
expect(page).to have_content 'Team Rocket'
end
end
end
The key difference is that feature specs test the app from a user story POV by driving a browser simulator while request specs are lighter weight and you just test against the raw response.
1 - How can I achieve the same as expect(assigns(:teams)).to
eq([team]). I guess I am asking, how can I check if I have an instance
variable in the index action with values [team]
Either use the assigns gem for legacy compatiblity or test the rendered output.
2 - If this method was moved to the gem, I read in the Github issues,
that the reason is: You shouldn't test it there, controller should
just test response, cookies etc. But I am confuse, since in relish you
can test the instance variable. Should I test it there or not? If not,
where? In my views/index_spec.rb, testing if I have all the teams?
If by Relish you mean RSpec, then its been taking a while for RSpec-rails to catch up to the state-of-art in Rails testing. But the same still applies. The offical recommendation of the RSpec team is to not use assigns and faze out controller specs in favor of request specs. View specs are not really relevant here - they are used if you want to test complex views in isolation.
3 - Alternative: Since TeamsController is a normal class, should I
create a spec in the spec/models/folder
spec/models/tasks_controller.rb and there test if the method index has
the instance variable #teams with the content that I want?
Just no. Controllers are not just normal classes. You can't just instantiate a controller with MyController.new, thats why controller tests have all that stubbing in place.
I have a still pretty simple Rails application that I want to develop using BDD with Cucumber and TDD with RSpec. Currently, I am hanging at a test where I want to check that if a new instance of an Organizer (that's the model I have) cannot be created due to a validation error. I would like to check that the errors Array of the object to be created is not empty so that I can be sure that error messages are available for showing them in the view.
require 'spec_helper'
describe OrganizersController do
render_views
describe "POST 'create'" do
describe "with invalid arguments" do
before(:each) do
request.env["HTTP_REFERER"] = organizers_new_path
#organizer_args = { :name => "" }
end
it "should return a non-empty list of errors" do
post 'create', :organizer => #organizer_args
#organizer.errors.empty?.should_not be_true
end
end
end
end
I am developing based on Rails 3.2.9 with RSpec 2 and cucumber-rails.
Any suggestions are appreciated. Thanks!
You should use assigns method to get instance variable from controller action:
assigns(:organizer).errors.empty?.should_not be_true
The latest preferred syntax is:
expect(assigns(:organizer).errors.empty?).to_not be_true
thanks for the answer guys but I'd like to suggest a slightly nicer syntax:
expect(assigns(:organizer).errors).to_not be_empty
(unrelated to the question 👇)
Basically whenever you have a method that ends with ? you'll have the corresponding rspec matcher that starts with be_ e.g.
1.odd? #=> true
expect(1).to be_odd
Currently in my spec/decorators/product_decorator_spec.rb, I have the following:
require 'spec_helper'
describe ProductDecorator do
let(:product) { FactoryGirl.create(:product) }
subject do
ProductDecorator.first
end
before do
product
end
it 'should render the name attribute with a link to the product page' do
subject.name.should == h.link_to(product.name, 'test')
end
end
When I run my spec I get the following:
F.....
Failures:
1) ProductDecorator should render the name attribute with a link to the product page
Failure/Error: subject.name.should == h.link_to(product.name, 'resr')
NameError:
undefined local variable or method `h' for #<RSpec::Core::ExampleGroup::Nested_2:0x007fbbf212c8b0>
# ./spec/decorators/product_decorator_spec.rb:15:in `block (2 levels) in <top (required)>'
Finished in 0.98531 seconds
6 examples, 1 failure
Failed examples:
rspec ./spec/decorators/product_decorator_spec.rb:14 # ProductDecorator should render the name attribute with a link to the product page
According to the documentation, specs placed in the decorator folder should have access to the helper method, however my spec does not. I've also tried manually tagging my specs, but doesn't seem to have any effect.
Thanks for looking.
if you want to access the helper, you can do it via your_decorator.h.link_to.
when you are setting the subject, you will need to make sure that the thing you are calling will get routed to the helper, there is nothing injected into your rspec example!
in your example it would be subject.h.link_to for calling a helper method.
i also think that there are a lot of wired things in your spec. your usage of let, subject and before are kind of disturbing for me...
here is a nice writeup about how to write clean rspec: http://eggsonbread.com/2010/03/28/my-rspec-best-practices-and-tips/
I've encountered the same issue, where calling helper methods on the decorator's helper proxy (.h) doesn't work in test (in Draper 1.3). I ended up working around it with this, though I'm not very pleased with it:
my_decorated_object.h.extend ApplicationHelper
Your mileage may vary depending on how many controller features you access in your helper.
I've been struggling with creating a login function that should be executed before any rspec test is run.
What I have right now is:
def login
post "/session", { "session[username]" => "bjones" }
end
in my spec_helper.rb file
Then, I have the following in one of my spec.rb files in the requests directory.
require 'spec_helper'
describe "Sessions" do
describe "GET /dashboards" do
login
it "displays the dashboard" do
get dashboard_path
puts response.body
end
end
end
However, when I try running the test, I get:
undefined method `post' for #<Class:0x4f08828> (NoMethodError)
I'm pretty new to rails and completely new to testing and rspec so maybe there's something fundamental I'm missing here. Basically, all I want to do is set that session variable so that when the test is run I will be logged in. Perhaps a different approach would be better? Or maybe I need to put that login function in a different place?
I came across this answer which was sort of useful but it's not for rspec so I'm having trouble understanding where such a helper function would go.
Try
let(:login) {
post "/session", { "username" => "bjones" }.to_json
}
This might have to be revised to use .to_json or not, depending on what content type the controller accepts.
I'm trying to test a presenter method using a Capybara RSpec matcher.
Lets say I have a method that renders a button. This would be the test I would write if I wasn't using capybara rspec matchers:
it "should generate a button" do
template.should_receive(:button_to).with("Vote").
and_return("THE_HTML")
subject.render_controls.should be == "THE_HTML"
end
Using capybara rspec matchers, I want to do this:
it "should render a vote button" do
subject.render_controls.should have_button('Vote')
end
This approach was proposed in this article http://devblog.avdi.org/2011/09/06/making-a-mockery-of-tdd/. In the article, the author explains it like this: "I decided to change up my spec setup a bit in order to pass in a template object which included the actual Rails tag helpers. Then I included the Capybara spec matchers for making assertions about HTML."
However, I don't understand this. How can you use capybara rspec matchers when render_controls only returns a content_tag?
Even though luacassus's answer is correct, I found what the problem was. I wasn't including capybara rspec matchers in the test. If you don't include Capybara rspec matchers, you will an error like this: undefined method has_selector? for ActiveSupport::SafeBuffer:0x9449590.
When you include the rspec matchers, there is no need to use Capybara String method, since rspec matchers match against a string already.
I leave here a more detailed example.
require_relative '../../app/presenters/some_presenter'
require 'capybara/rspec'
describe 'SomePresenter'
include Capybara::RSpecMatchers
let(:template) { ActionView::Base.new }
subject { Presenter.new(template) }
it "should render a vote button" do
subject.render_controls.should have_button('Vote')
end
end
Check out Capybara.string method: http://rubydoc.info/github/jnicklas/capybara/master/Capybara#string-class_method
With this method you should be able to write something like that:
subject { Capybara.string(presenter.render_controls }
it { should have_button('Vote') }