Why don't RSpec's methods, get , post , put , delete work in a controller spec in a gem (or outside Rails)?
Because these methods don't belong to Rspec but to Rails.
When you describe a controller, it inherits from Spec::Rails::Example::ControllerExampleGroup, which inherits FunctionalExampleGroup, which inherits the rails' ActionController::TestCase.
If you look at ActionController::TestCase's documentation you'll find that's where the get/post/put/delete methods are defined.
So if you want to get access to these methods outside of Rails, you need to redefine them.
Rails controller test 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.
Testing rails controller with rspec
Your call to the request should be after any should_receive
. It's a tense thing. So it kind of reads like this, "Category should receive something, when this happens". "This happens" refers to the request.
it "should find the category by url" do
Category.should_receive(:find).with...
get "show", { your params, if you're sending some in }
end
Also, you want to go the way of a request vs calling the controller method itself, for this particular test at least.
So
post "action_name"
get "action_name"
update "action_name"
delete "action_name"
instead of
controller.action_name
Rails controller rspecs fail with RoutingError for spec/controllers but pass for individual tests
Figured it out.
The ActionController::RoutingError was a result of another controller spec which was calling:
routes.draw
This call wiped out the routes for subsequent specs. The solution for now (thanks to:
http://pivotallabs.com/adding-routes-for-tests-specs-with-rails-3/ ) was to add:
after do
# be sure to reload routes after the tests run, otherwise all your
# other controller specs will fail
Rails.application.reload_routes!
end
Also helpful were:
https://github.com/rspec/rspec-rails/issues/817
https://github.com/rspec/rspec-rails/issues/636
Hope this helps and good luck!
Set Rspec default GET request format to JSON
before :each do
request.env["HTTP_ACCEPT"] = 'application/json'
end
RSpec test destroy method (Rails Tutorial 3.2 Ch. 9, Ex. 10)
You are confusing rspec-rails request specs which are integration tests and are executed in a simulated browser and controller specs which test controller in isolation. delete(action, *args)
(and get
, post
and so on) - is a method simulating request from ActionController::TestCase, so it's not available in your test.
So your only option is to simulate a click in a browser. I don't know how you hide your delete link, if the html is there but hidden you should be able to click it. If it's not there (removed on the server side when generating view) you can use capybara's page.execute_script
(but you have to enable javascript for this example :js => true
). You can either add the link back:
page.execute_script("$('body').append("<a href="/users/1" data-method="delete" rel="nofollow">Destroy</a>")")
or make ajax call:
page.execute_script("$.ajax({type:'DELETE',url:'/users/1'})")
Didn't test but something like this should work.
Related Topics
Simple Ruby Input Validation Library
How to Make a Gemset in Rvm the Default
How to Install Ruby 1.9.3 on Ubuntu Without Rvm
"Don't Run Bundler as Root" - What Is the Exact Difference Made by Using Root
How to Sort by Multiple Conditions with Different Orders
Do Ruby 1.8 and 1.9 Have the Same Hash Code for a String
Why Do I Get an "Undefined Method for 'Has_Attached_File' When Installing Paperclip
Removing Child Root Nodes in Rabl
Most of My Assets Suddenly Return 404 After a Push to Heroku
Connecting Using Https to a Server with a Certificate Signed by a Ca I Created
Learning Ruby: Recommended Blogs to Read
Error Installing a Pod - Bus Error at 0X00000001045B8000
What Are the Conventional Gem Paths for Ruby Under Os X 10.5
Sinatra Static Assets Are Not Found When Using Rackup
How to Get a List of Files That Have Been 'Required' in Ruby