post to a different controller in an rspec test - ruby-on-rails

How do I post to a different controller than the one the test script is currently pointing at?
Example:
in user_controller_spec.rb
it "should just post to users" do
post :create, #params # this goes to the users controller
end
I want to do something like:
it "should post to user and people to do some integration testing" do
post :create, #params # this goes to the users controller still
post 'people', :create, #params # this goes to the people controller
end
ps: i don't want to setup cucumber

Controller specs are wrappers for Rails functional tests, which don't support multiple requests or controllers. What you want is an RSpec request spec (rails 3) or an integration spec (rails 2). These wrap Rails integration tests, which does support multiple requests with multiple controllers (multiple sessions, even), but they work a bit differently from controller specs. You have to use the full path (so get new_thing_path), and you can't stub anything on the controller (because there is no controller before you make a request).
See http://relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec and http://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest.html for more info.

There is a way if you assigning the value of #controller before call test method. Example
def setup
#controller = UserController.New
do user stuff
#controller = ThisController.New
do test intented for this controller
end

Based on other answer, but more safe.
Store current controller instance and create a new with the required controller. Finally, replace new controller instance with the old stored instance.
def setup
old_controller = #controller
#controller = UserController.new
# do user stuff
#controller = old_controller
end

Related

Rails engine testing controller using helper method of main app in rspec

I have one main rails app, and I have an engine within it, now the engine has a different test, I need to test one controller which is using main app MailerHelper module. How can I access in my spec.
My engine's user controller
module ControlPanel
class UserController < ApplicationController
include MailerHelper
def index
#users = User.all
end
end
end
I am using MailerHelper have many other methods which sent mails. As this is controller is in my engine control panel, following is specification in my engine
require 'rails_helper'
describe ControlPanel::UsersController, type: :controller do
describe '#index' do
it 'should display all users' do
get :index
expect(response).to render_template('index')
end
end
end
The test case is just simple, but as in my engines User controller I am using my main application helper, so it can't take it, I have seen many replies but nothing working, like including in rails_spec and ::MailerHelper etc.

Testing a Rails controller action with RSpec’s post method

I have a very simple controller:
class ThingsController < ApplicationController
def create
Thing.do_stuff
end
end
…And a very simple spec:
require "rails_helper"
describe ThingsController do
describe "POST #create" do
it "does stuff with things" do
expect(Thing).to receive(:do_stuff)
controller.create # This works
post :create # This does not work
end
end
end
I am not running the direct invocation and the post request at the same time. Invoking the action on the controller directly passes the assertion, but invoking the action through the post method does not. It appears do_stuff is never called on Thing. Why might that be?
I discovered what my issue was.
Invoking the controller directly keeps the spec isolated and ignores things like a before_action in the ApplicationController.
When we start using the post method, it’s really an integrated test and hits things like authentication. I couldn’t hit my controller method because my test user wasn’t signed in.

Rails 4 engine controller redirect fails unit testing

I am building a Rails 4 engine that provides some controllers and models that will then be used by several of our apps. I am in the process or writing the unit tests, and I am having problems with the actions in the controllers that make a redirect_to.
In the controller that I am testing, I have the following actions:
def index
end
def new
#block = GlobalIpBlock.new
end
def create
#block = GlobalIpBlock.new(create_params)
if #block.save
flash[:success] = "The IP has been successfully blocked."
redirect_to action: 'index'
else
render 'new'
end
end
and in the controller test I have these two tests:
test "should get new block" do
get :new, use_route: :watchdog
assert_response :ok
assert_not_nil assigns(:block)
end
test "should create global ip block" do
assert_difference('GlobalIpBlock.count') do
post :create, block: {some_param: 'some value'}, use_route: :watchdog
end
assert_redirected_to :index
end
The first test passes but the second throws an error:
ActionController::UrlGenerationError: No route matches {:action=>"index"}
I have no routes created in the engine and in the routes of the dummy test app it only mounts the engine. The reason for that is that I want the hosting apps to provide their own routes to the controllers/actions of the engine.
Still, this doesn't seems to be the issue here since the test for the action new passes. Furthermore, I have tried creating the routes in the engine by doing:
resources :global_ip_blocks, except: [:edit, :update]
but that didn't help, neither did doing that in the routes of the dummy test app.
I am guessing the redirect_to is not finding the route in the same way that removing use_route: :watchdog from the get/post in the tests fail but how can I fix that? Is there something like a global way of telling unit tests to use_route: :watchdog?
You should be able to fix this using:
class MyControllerTest < ActionController::TestCase
def setup
#routes = MyEngine::Engine.routes
end
end
Also, look out for the deprecation notice in the logs due to the use of use_route which is like:
DEPRECATION WARNING: Passing the use_route option in functional tests are deprecated. Support for this option in the process method (and the related get, head, post, patch, put and delete helpers) will be removed in the next version without replacement. Functional tests are essentially unit tests for controllers and they should not require knowledge to how the application's routes are configured. Instead, you should explicitly pass the appropiate params to the process method. Previously the engines guide also contained an incorrect example that recommended using this option to test an engine's controllers within the dummy application. That recommendation was incorrect and has since been corrected. Instead, you should override the #routes variable in the test case with Foo::Engine.routes. See the updated engines guide for details.

In a Rails controller test, how to access a different controller action?

In a functional test, I want to call an action in another controller.
You have to set the #controller instance variable to the controller that should be used.
Example usage in a test helper method (of course you don't need to use it in a helper method - you can use it right in your test method):
def login(user_name='user', password='asdfasdf')
# save the current controller
old_controller = #controller
# use the login controller
#controller = LoginController.new # <---
# perform the actual login
post :login, user_login: user_name, user_password: password
assert_redirected_to controller: 'welcome', action: 'index'
# check the users's values in the session
assert_not_nil session[:user]
assert_equal session[:user], User.find_by_login('user')
# restore the original controller
#controller = old_controller
end
Answered by Jonathan Weiss, in 2006 on ruby-forum: post() to other controller in functional test?
It should be noted that, for the most part (probably >99.9% of the time), one should use integration tests (aka feature tests) for testing inter-controller behaviour.

What belongs in a controller spec and what belongs in a request spec?

Using Rspec, I am trying to create a controller spec for my API, as well as a request spec. The problem is I don't understand what part of the test goes in each spec.
For simplicity, let's say my controller looks like follows:
class Api::V1::ItemsController < ApplicationController
def index
if params[:user_id]
user = User.find(params[:user_id])
#items = user.items
else
#items = Item.all
end
end
end
It uses RABL to render the response.
For a controller as simple as this, what does the controller spec look like? What does the request spec look like? At what point to I verify that the API response JSON is what I expect? etc.
For the controller spec, I'd write tests like this:
it "renders with a user_id" do
get :index, id: #user.id
assigns(:items).should == #user.items
response.should be_success
response.should render_template("index")
end
One for the user_id path, and one without. I leave setting up #user to you; you can use fixtures, or FactoryGirl, instantiate one in a before block, or just stub/mock it all out. It's good to avoid hitting the database at all if you can. That can be a little trickier with things like Item.all and associations, which are harder to wire up without either stubbing or persisting to the test DB. Mocking and stubbing makes it easy, but also makes your tests a bit more brittle should the models change.
For the request spec, I'd create real DB objects and do something like this, using Capybara:
it "lists all items without a user_id" do
visit foo_path
current_path.should == foo_path
page.should have_content(item1.name)
page.should have_content(item2.name)
end
..and similar for the path with user_id. Without knowing your data and how you're rendering it, I can't be more specific. If you're using JSON, you should check the entire JSON response here. I wouldn't check the JSON response in the controller spec, though; the controller's job is just to shuffle the data to the right view. You can test the view in isolation too, and then test the whole end-to-end scenario in your request spec.
For the controller spec, you'd set up mocks that will handle the authentication of the request. You'd only want to test that if params[:user_id] is passed, then #items is set to the user items, and if not, then all items.
For the request spec, you will simply include some test to verify the process of authentication.
If you ask me, I'll verify the api response as a json in both tests.

Resources