Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to allow exceptions to bubble up in rspec request specs

I want to know how to simply allow all exceptions to bubble up to rspec in the middle of a request spec.

I hope an example will make this clear. Let's say I have the following request spec and corresponding application code:

# user_browses_posts_spec.rb
feature 'User views a post' do
  scenario 'this should fail with route missing' do
    FactoryGirl.create(:post)
    visit(root_path)
    click_on('View Post')
  end
end

# config/routes.rb
MyApp::Application.routes.draw do
  root to: 'posts#index'
  # notice I have not defined a :posts resource, so post_path should raise NoMethodError
end

# assume a totally standard app/controllers/posts_controller.rb

# app/views/posts/index.html.erb
<% @posts.each do |post| %>
  <%= link_to 'View Post', post_path(post) %>  # this line should fail
<% end %>

When I run the test, what I see is:

Failure/Error: click_on('View Post')
Capybara::ElementNotFound:
  no link or button 'View Post' found

This is because when the NoMethodError was raised from within the app, the spec runner did not perceive a problem because it saw the normal Rails development error page (with the error message, backtrace, params, etc).

But what I want to see in my terminal is:

Failure/Error: visit(root_path)
NoMethodError:
  undefined method `post_path' for #<PostsController:0x007fea60a779c8>

My question, then, is how can I disable that rails error handling entirely, so the NoMethodError bubbles all the way up to rspec?

Thanks!

like image 843
Luke Griffiths Avatar asked Aug 22 '12 20:08

Luke Griffiths


People also ask

What is RSpec rails gem?

RSpec is a testing framework written in Ruby to test Ruby code. To get started using RSpec with Rails, add it to the Gemfile. group :development, :test do gem 'rspec-rails' end. Next finish setting it up by running bundle install in your project directory and then. rails generate rspec:install.


1 Answers

I don't believe this is possible. However, I also don't think you'd really want visit() to bubble up controller errors even if it could, since visit() is intended for functional testing.

Request specs run in their own context, meaning that the visit() method hits the URL as if it were a browser, rather than calling the controller method directly. Thus, the request will go through all the Rack middleware and the Rails routing layer. The error handling there behaves the same as it would in the real world, which is a good thing for 'functional' tests like these.

Consider what happens when you manually test something like this. When you go to a specific (broken) URL, what shows up in the browser? One of two things:

  • in production, it's generally a static error page
  • in development, a generated page containing text describing an error (with a stack trace)

In both cases the browser does, in fact, receive an HTML page.

Next, you look at this page and say to yourself "Hey, that's not the page I was expecting!". At this point, you've determine what went wrong: the page isn't what you expected. You'd then have to investigate in the logs, or read the stack trace on the development mode error page, to try to determine why this went wrong.

The spec is essentially doing the same thing, but automated. The visit() call worked just fine, and it did produce some kind of HTML page. The test then tries to validate that this contains some specific content by trying to click a link, and this is where the test fails. This step is conceptually the same as you saying "Hey!" only after the page has loaded and it doesn't contain what you expected.

You can unit test the controller methods directly if you'd like. Those will tell you what went wrong at a much more granular level.

Functional test:

  • what: the page didn't contain the expected content
  • why: the controller action threw an error

Unit test:

  • what: the controller action threw an error
  • why: somebody forgot to put that file in the project (or whatever)
like image 78
Benjamin Cox Avatar answered Oct 06 '22 18:10

Benjamin Cox