Putting RSpec Test Cases in the Right Place - ruby-on-rails

I'm trying to use RSPec to test my Ruby on Rails 3.2 app.
When I generated the controller, some specs were created for the views and the controller. I tried adding the following test to the controller spec:
it "should have h1 of Home" do
visit '/home/index'
page.should have_selector('h1', text: "Home")
end
But couldn't get it to pass.
When I ran rails generate integration_test home and put the same test in the homes_spec, the test works fine.
Why does it matter where the spec goes?
Thanks

According to the doc:
If you would like to use webrat or capybara with your request specs,
all you have to do is include one of them in your Gemfile and RSpec
will automatically load them in a request spec.
And request specs live in integration and requests folders.
Simply.

Related

Rails controller test with Rspec

I an trying to organize code by making partial html.erb files that are shared frequently(e.g. _form.html.erb)
I want to check whether my partial code works well with different models/controllers, so I am manually doing CRUD from the views.
It would be nicer to test my code automatically using Rspec but I have no idea. Can anyone give me some guidance how to test controller code with Rspec?
To test controller and views together you write feature specs and request specs .
Request specs are lower level specs where you send HTTP requests to your application and write expectations (aka assertions in TDD lingo) about the response. They are a wrapper around ActionDispatch::IntegrationTest. Request specs should be considered the replacement for controller specs, the use of which are discouraged by by the RSpec and Rails teams.
# spec/requests/products_spec.rb
require 'rails_helper'
RSpec.describe "Products", type: :request do
describe "GET /products" do
let!(:products) { FactoryBot.create_list(:product, 4) }
it "contains the product names" do
get "/products"
expect(response).to include products.first.name
expect(response).to include products.last.name
end
end
end
Feature specs are higher level specs that focus on the user story. They often serve as acceptance tests. They use a browser simulator named Capybara which emulates a user clicking his way through the application. Capybara can also run headless browsers (headless chrome, firefox, phantom js, webkit etc) and "real" browsers through selenium. The minitest equivalent is ActionDispatch::SystemTestCase but RSpec features do not wrap it (it took minitest/testunit years to catch up here).
# Gemfile
gem 'capybara'
# spec/features/products_spec.rb
require 'rails_helper'
RSpec.feature "Products" do
let!(:products) { FactoryBot.create_list(:product, 4) }
scenario "when a user views a product" do
visit '/'
click_link 'Products'
click_link products.first.name
expect(page).to have_content products.first.name
expect(page).to have_content products.first.description
end
end
This specs tests the products#index and products#show action as well as the root page and the associated views.
Both types of specs have their strengths and weaknesses. Feature tests are good for testing large swaths of the application but are heavy. Request specs are faster and its easier to replicate a specific request that causes a bug/issue but you're basically just matching HTML with regular expressions which is highly limited.
To check whether partial code works well with different models/controllers. You can add render_views in controller specs.
How to test controller code with Rspec?
Read the official doc https://relishapp.com/rspec/rspec-rails/docs/controller-specs
And this page may help: https://thoughtbot.com/blog/how-we-test-rails-applications

Ruby on Rails : how to test a controller?

I'm learning Ruby.
I'm now trying to test one of my controller.
My test file is myapp/test/testing.rb while my controller is located at myapp/app/controllers/test_controller.rb.
The content of testing.rb is
data = testController.mymethod()
puts(data)
But when doing
ruby myapp/test/testing.rb
in the terminal, I get a warning :
Traceback (most recent call last):myapp/test/testing.rb:6:in `':
uninitialized constant testController (NameError)
Could someone explain me what i'm doing is wrong and how I should do this ?
Thanks !
The currently accepted way to test rails controllers is by sending http requests to your application and writing assertions about the response.
Rails has ActionDispatch::IntegrationTest which provides integration tests for Minitest which is the Ruby standard library testing framework. Using the older approach of mocking out the whole framework with ActionDispatch::ControllerTest is no longer recommended for new applications.
This is your basic "Hello World" example of controller testing:
require 'test_helper'
class BlogFlowTest < ActionDispatch::IntegrationTest
test "can see the welcome page" do
get "/"
assert_select "h1", "Welcome#index"
end
end
You can also use RSpec which is different test framework with a large following. In RSpec you write request specs which are just a thin wrapper on top of ActionDispatch::IntegrationTest.
RSpec is DSL written on Ruby special for this purpose. You have to be familiar with it to write tests of your code. As for RSpec team note:
`
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. In Rails 5, request specs are significantly faster than either request or controller specs were in rails 4.
So you have to write requestspecs for your controller. Something like this:
spec/controllers/users_controller_spec.rb
RSpec.describe "Test", type: :request do
describe "request list of all tests" do
user = User.create(email: 'test#user.com', name: 'Test User')
get users_path
expect(response).to be_successful
expect(response.body).to include("Test User")
end
end
Something like this. Hope it will help

Rspec 3.X: native built in html matchers? Or do we have to use Capybara?

I've been reading a ton of docs and SO questions/ answers on all the changes as Rspec has evolved, want to be sure of the answer...
My goal is to use native Rspec-rails (I have 3.2.2) to do integrated controller/view tests that look for 1) CSS classes and 2) ID selectors. In other words given this view snippet:
<!-- staticpages/dashboard -->
<div class="hidden">Something</div>
<div id="creation">This</div>
This should pass (however it should be semantically written):
describe StaticpagesController do
render_views
it "should find everything" do
get :dashboard
expect(response.body).to have_selector("div#creation")
expect(response.body).to have_css("hidden")
expect(response.body).to_not have_selector("div#nothinghere")
end
end
I would like to do this without additional gems like Capybara; is that possible?
Here's a high level of what I've learned so far:
in Rspec 1, the have_tag feature allowed you to do this (http://glenngillen.com/thoughts/using-rspec-have-tag)
in Rspec 2, the have_tag was replaced with webrat's have_selector (have_tag vs. have_selector)
in Rspec 3, webrat support has been removed (http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/)
In my own experimentation, the code above generated:
Expect<long response.body here>.to respond to `has_selector?`
So that has indeed been deprecated. Still, I'd love to know if there's some other way to do this that I don't know about.
IF it turns out I need Capybara to do these fancy matchers, is there a way to do this in my integrated controller/view specs? My understanding is that I have to add type: :feature to the describe StaticpagesController line to use Capybara's matchers. However, the minute I do that, render_views is no longer available (since it's limited to type: :controller). Note, render_views also dies if, per this post (https://www.relishapp.com/rspec/rspec-rails/v/2-99/docs/controller-specs/use-of-capybara-in-controller-specs), I manually include Capybara::DSL into my controller spec. Anyway, I would really like to not have to rewrite my current controller specs into a bunch of feature specs...
It would seem that you want feature specs (with Capybara) more than controller specs as you're not testing any of the things controller specs are typically used to test such as:
whether a template is rendered
whether a redirect occurs
what instance variables are assigned in the controller to be shared with the view
the cookies sent back with the response
Also, you probably want to consider writing feature specs for new apps over controller specs since controller tests will probably be dropped in Rails 5 in favor of the writing of integration/feature tests.
For a description of the different kinds of specs that you could write, and what they're typically used for,
see this SO answer.

Understanding RSpec testing

I've been searching around for a while, and did not find a clear explanation about testing structure in RoR. (I'm learning with Michael Hartl's book).
Since I'm using rspec for testing, do I need to keep the "test" folder anymore ?
Is the "spec" folder structure strictly assigned to specific test purposes ?
When "generating" a test script, does it do something else than creating the script file ? (i.e. could I create it by hand ?)
The 2nd question comes from this error:
undefined method `visit' for #<RSpec::Core::ExampleGroup: ...
which occurs as soon as my very simple test file is not in /spec/requests (but in /spec/views):
require 'spec_helper'
describe "Home page" do
subject { page }
###1
describe "title" do
before { visit root_path }
it { should have_selector('h1', text: 'Welcome') }
it { should have_selector('title', text: 'Home') }
end
end
1) No, you don't need the test folder. Only spec, unless you're using Test::Unit.
2) You can order spec however you want. The general suggested format is
spec/
controllers/
views/
models/
lib/ #Only if you have stuff in lib you need to test
spec_helper.rb
with names like spec/controllers/items_controller_spec.rb, and spec/models/item_spec.rb
3) I'm not sure what you mean. For RSpec, you just need to make a file that uses its syntax to test certain pieces of your Ruby code. It doesn't create a script -- you do.
For your last question about visit:
It seems like you're trying to use a Capybara method (visit). Capybara takes care of visiting your site in a browser, not RSpec.
No
See Below
No, only create the file
There is one specific folder structure you need to use in order to run Capybara acceptance tests. These are tests that have "visit" in them
Capybara 1.0
spec/requests
Capybara 2.0
spec/features
No, my understanding is that the test folder is generally used for Test::Unit stuff. You can delete or keep it; doesn't hurt to leave it.
There's a pretty specific way they want you to organize that stuff. If you have app/models/user.rb, for example, your spec should be in spec/models/user_spec.rb.
I don't know of anything else it does besides creating the test script. You can totally create those by hand, and I do all the time.

capybara: post, get methods not working when changing name of requests directory to features

After upgrading to the latest version of Capybara, all of my visit methods stopped working so I followed a solution presented by some people which was to rename the requests spec directory to "features". Now my visit methods are working again but any get or post method in a request spec causes this error:
undefined method `get' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1::Nested_1::Nested_1::Nested_1:0x007f9cce9adc20>
Here's the code that triggers the error:
describe "getting posts" do
before { get(forum_posts_path) }
it "should respond with a 200" do
response.response_code.should == 200
end
end
Any workaround for this?
You don't rename the spec/requests directory to spec/features: you have both:
Tests that use the Capybara DSL (visit etc) and usually assert against page go in spec/features.
Tests that use the rack-test DSL (get etc) and usually assert against response go in spec/requests
See this StackOverflow answer for details, specifically the external links there.

Resources