Why Don't Rspec's Methods, "Get", "Post", "Put", "Delete" Work in a Controller Spec in a Gem (Or Outside Rails)

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



Leave a reply



Submit