jonleighton / focused_controller

MIT License
468 stars 27 forks source link

Test with RSpec and custom render #22

Closed ccocchi closed 11 years ago

ccocchi commented 11 years ago

Hi,

I have the following action

module ConnectorsController
...

class Create < Action
 expose(:connector)

 def call
   @connector = Connector.new(params[:connector])
   @connector.save
   render :json => { :status => 'ok' }
 end
end

If i tried to call it with manually, for example with curl, everything works perfectlly. But when I tried to test it with RSpec

 Failure/Error: subject.call
  RuntimeError:
    ActionController::RackDelegation#content_type delegated to @_response.content_type, but @_response is nil

Here's the test I used, pretty basic

describe ConnectorsController::Create do
 it "should render JSON" do
  subject.params = { :connector => {} }
  subject.call
  puts response.body
 end
end

I also tried to replace the JSON by render :action => 'show' but without success.

jonleighton commented 11 years ago

That's odd. We have similar tests in our own code base without trouble. Can you share the stack trace?

ccocchi commented 11 years ago

The issue comes from the association with RSpec helper render_views (i'm using RABL to render JSON). Here's the backtrace on a dummy app with a Post model and a create method, just calling subject.call

PostsController PostsController::Create should render JSON
     Failure/Error: controller.call
     RuntimeError:
       ActionController::RackDelegation#status= delegated to @_response.status=, but @_response is nil: #<PostsController::Create:0x00000100ae10e0 @_routes=nil, @_action_has_layout=true, @_headers={"Content-Type"=>"text/html"}, @_status=200, @_request=#<ActionController::TestRequest:0x00000100acac00 @env={"rack.version"=>[1, 1], "rack.input"=>#<StringIO:0x00000105e14000>, "rack.errors"=>#<StringIO:0x00000105e140a0>, "rack.multithread"=>true, "rack.multiprocess"=>true, "rack.run_once"=>false, "REQUEST_METHOD"=>"GET", "SERVER_NAME"=>"example.org", "SERVER_PORT"=>"80", "QUERY_STRING"=>"", "rack.url_scheme"=>"http", "HTTPS"=>"off", "SCRIPT_NAME"=>"", "CONTENT_LENGTH"=>"0", "action_dispatch.routes"=>#<ActionDispatch::Routing::RouteSet:0x00000105dcf8b0>, "action_dispatch.parameter_filter"=>[:password], "action_dispatch.secret_token"=>"e5048ed1d5ee25b988533437f6a1981a146b33f63165a9241953ad72585bae990875b8b9f689e91bd2370fdccc4b57f3727c6802a162ffbfee341566097cbf9a", "action_dispatch.show_exceptions"=>false, "action_dispatch.show_detailed_exceptions"=>true, "action_dispatch.logger"=>#<ActiveSupport::TaggedLogging:0x00000105f0f9f0 @logger=#<ActiveSupport::BufferedLogger:0x00000105f0fb80 @log_dest=#<File:/Users/Coki/code/test_app/log/test.log>, @log=#<Logger:0x00000105f0fb58 @progname=nil, @level=0, @default_formatter=#<Logger::Formatter:0x00000105f0fb08 @datetime_format=nil>, @formatter=#<Logger::SimpleFormatter:0x00000105f0fa18 @datetime_format=nil>, @logdev=#<Logger::LogDevice:0x00000105f0fab8 @shift_size=nil, @shift_age=nil, @filename=nil, @dev=#<File:/Users/Coki/code/test_app/log/test.log>, @mutex=#<Logger::LogDevice::LogDeviceMutex:0x00000105f0fa90 @mon_owner=nil, @mon_count=0, @mon_mutex=#<Mutex:0x00000105f0fa40>>>>>, @tags={#<Thread:0x00000100887290 run>=>[]}>, "action_dispatch.backtrace_cleaner"=>#<Rails::BacktraceCleaner:0x00000100ae2828 @filters=[#<Proc:0x00000100ae2760@/Users/Coki/.rbenv/versions/1.9.3-p125/lib/ruby/gems/1.9.1/gems/railties-3.2.8/lib/rails/backtrace_cleaner.rb:10>, #<Proc:0x00000100ae26c0@/Users/Coki/.rbenv/versions/1.9.3-p125/lib/ruby/gems/1.9.1/gems/railties-3.2.8/lib/rails/backtrace_cleaner.rb:11>, #<Proc:0x00000100ae2698@/Users/Coki/.rbenv/versions/1.9.3-p125/lib/ruby/gems/1.9.1/gems/railties-3.2.8/lib/rails/backtrace_cleaner.rb:12>, #<Proc:0x00000100ae1950@/Users/Coki/.rbenv/versions/1.9.3-p125/lib/ruby/gems/1.9.1/gems/railties-3.2.8/lib/rails/backtrace_cleaner.rb:26>], @silencers=[#<Proc:0x00000100ae18b0@/Users/Coki/.rbenv/versions/1.9.3-p125/lib/ruby/gems/1.9.1/gems/railties-3.2.8/lib/rails/backtrace_cleaner.rb:15>]>, "HTTP_HOST"=>"test.host", "REMOTE_ADDR"=>"0.0.0.0", "HTTP_USER_AGENT"=>"Rails Testing", "rack.session"=>{}, "rack.session.options"=>{:key=>"rack.session", :path=>"/", :domain=>nil, :expire_after=>nil, :secure=>false, :httponly=>true, :defer=>false, :renew=>false, :sidbits=>128, :cookie_only=>true, :secure_random=>SecureRandom, :id=>"137ec1137dc4962221d22288dd578cb5"}}>, @_response=nil, @_params={:post=>{:title=>"Foo"}, :format=>:json}, @post=#<Post id: nil, title: nil, body: nil>, @_prefixes=["posts", "posts", "application", "application"]>
     # ./app/controllers/posts_controller.rb:10:in `call'
     # ./spec/posts_controller_spec.rb:9:in `block (3 levels) in <top (required)>'

Don't know if it is the "normal" behavior not to render views inside a unit controller test. Tell me if you want me to put the dummy app online, i'll will try to look at RSpec render_views to find a way to fix this.

jonleighton commented 11 years ago

@ccocchi are you writing a unit test or a functional test? if the former, you cannot render views, which would take the test outside the realm of unit testing.

ccocchi commented 11 years ago

Yes that's what I've been saying myself after re-reading what I wrote.

Thank you for your help !