rspec testing for content - ruby-on-rails

I'm using Rspec to test the contents of a view in my Controller spec. I'm trying to test that all Product entries have their descriptions displayed on the page.
describe StoreController do
render_views
describe "GET 'index'" do
before(:each) do
get :index
end
it "should display the product list" do
Product.all.each do |product|
response.should have_selector("p", :content => product.description)
end
end
end
end
This doesn't seem to work, however, as the test passes regardless of what's in the view. Still very new to Rails, so it's probable that I'm doing something completely wrong here. How can I make the code test for the presence of each product description in the StoreController index view?

Personally I wouldn't test the contents of the view in the controller. I'd just test the outgoing output of the controller actions and any support methods. I'd put view related tests into the view specs.
If you look in the ones that are generated by rails you should see some examples of how to assert content there.

I figured it out. The problem was that my test database was not populated with any Products, so the each block was never executing. Fixed it by using Factory Girl to make sure there was data in the test database beforehand.

Related

Assigns has been extracted to a gem. Use gem 'rails-controller-testing'. Is there an alternative for the gem?

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.

How to change controller RoR

I'm writing some tests for my simple Ruby on Rails application and found myself needing to use the POST method for one controller inside of another controller's test.rb file. The error I receive informs me that no such route exists and that is because it is trying to use the controller pertaining to the test file. I want to manually define a different controller for this one call to POST.
test "Rcomment - Destroy" do
article = articles(:valid)
article.save
comment = comments(:valid)
comment.save
post :create, article_id: article.id, comment: comment.attributes
post :create, article.attributes <--- "Should use article controller not comments controller"
get(:destroy, { 'id' => comment.id, 'article_id' => article.id}, nil)
assert_response :redirect
assert_not_nil assigns(:comment)
end
A controller test (that is one that sublclasses from ActionController::TestCase) can only test one controller class, and should only really be making a single request
If you want to make multiple requests across multiple controllers then you should use an integration test. If (as it looks) you are just trying to create some test data for your test then you can just create the data directly. By the looks of it it looks like you're using fixtures - You don't need to do a post to create those a second time.

Is is it a good idea to test if records were displayed on a page with feature specs

I have a page where user should see items he created. So I decided I want a feature test for that(with rspec and capybara).
What I wanted to do something like this:
require 'spec_helper'
feature "Accounts pages" do
scenario "user views his accounts" do
user = create(:user)
sign_in user
expect(current_path).to eq accounts_path
expect(page).to have_content "Your accounts"
#here I want to check if records were loaded and/or displayed on page
end
end
So my questions:
Is it a good idea to test if records were loaded on a specific page in a feature spec, or should I write controller spec for that?
If it's ok to go with feature spec here, what would be idiomatically correct way to do that?
You can't use controller spec to test if records are shown by any page as controller doesn't care what's displayed and what's not. In that case using feature spec sounds just fine.

Stubbing named_scope in an RSpec Controller

I haven't been able to find anything for a situation like this. I have a model which has a named scope defined thusly:
class Customer < ActiveRecord::Base
# ...
named_scope :active_customers, :conditions => { :active => true }
end
and I'm trying to stub it out in my Controller spec:
# spec/customers_controller_spec.rb
describe CustomersController do
before(:each) do
Customer.stub_chain(:active_customers).and_return(#customers = mock([Customer]))
end
it "should retrieve a list of all customers" do
get :index
response.should be_success
Customer.should_receive(:active_customers).and_return(#customers)
end
end
This is not working and is failing, saying that Customer expects active_customers but received it 0 times. In my actual controller for the Index action I have #customers = Customer.active_customers. What am I missing to get this to work? Sadly, I'm finding that it's easier to just write the code than it is to think of a test/spec and write that since I know what the spec is describing, just not how to tell RSpec what I want to do.
I think there's some confusion when it comes to stubs and message expectations. Message expectations are basically stubs, where you can set the desired canned response, but they also test for the call to be made by the code being tested. In contrast stubs are just canned responses to the method calls. But don't mix a stub with a message expectation on the same method and test or bad things will happen...
Back to your question, there are two things (or more?) that require spec'ing here:
That the CustomersController calls Customer#active_customers when you do a get on index. Doesn't really matter what Customer#active_customers returns in this spec.
That the active_customers named_scope does in fact return customers where the active field is true.
I think that you are trying to do number 1. If so, remove the whole stub and simply set the message expectation in your test:
describe CustomersController do
it "should be successful and call Customer#active_customers" do
Customer.should_receive(:active_customers)
get :index
response.should be_success
end
end
In the above spec you are not testing what it returns. That's OK since that is the intent of the spec (although your spec is too close to implementation as opposed to behavior, but that's a different topic). If you want the call to active_customers to return something in particular, go ahead and add .and_returns(#whatever) to that message expectation. The other part of the story is to test that active_customers works as expected (ie: a model spec that makes the actual call to the DB).
You should have the array around the mock if you want to test that you receive back an array of Customer records like so:
Customer.stub_chain(:active_customers).and_return(#customers = [mock(Customer)])
stub_chain has worked the best for me.
I have a controller calling
ExerciseLog.this_user(current_user).past.all
And I'm able to stub that like this
ExerciseLog.stub_chain(:this_user,:past).and_return(#exercise_logs = [mock(ExerciseLog),mock(ExerciseLog)])

Should A Custom Method Be Used In The before(:each) Section

I have a method on the model Category called create_main used to create main categories. Should I use this method in the before(:each) section even though the method itself has to be tested, or should the main category be created manually using rails built in functionality.
It should be possible to partition your Examples into two Example Groups, one group where before(:each) is called with create_main, and you use this to test everything except create_main. Then, you have another subset, where before(:each) does not call create_main, and here you test create_main.
In your case, I think you could try something like the following:
describe Category, " without a main category" do
before(:each) do
# No call to create_main here
end
it "should create the main category" do
# Here we test that create_main is working
end
end
describe Category, " with a main category already created" do
before(:each) do
# This time, we do call create_main to set up the object as necessary
end
# More examples go here that depend on create_main
end
Give that a shot. I'm not 100% sure it works, but I've seen similar setups in the past.

Resources