I am trying to write Rspec test case for the submit method in the app/controllers.sample.rb file.
User class is defined in the lib/classes folder. User object is created in the session.rb file in app/controllers/concerns which is autoloaded during creation of new session.
The user method in the session.rb gets the user parameters from another API.
Here I am finding it difficult to create the User object using Rspec, it is always returning error at
list = user.get_list
I have given the sample set of code I have written for the test case.
Could anyone help how to instantiate the User object in concerns/session.rb from rspec ?
app/controllers/concerns/session.rb
def user
if user
user
else
begin
rest_resource = RestClient::Resource.new(ENV['SESSION_API'], :verify_ssl => false)
data = rest_resource.get Authorization: request.headers['Authorization']
rescue RestClient::Exception => e
#error = JSON.parse(e.response, symbolize_names: true)
return nil
end
self.user = User.new(current_user,request.headers['Authorization'] )
end
end
spec/controllers/rspec_sample.rb
describe "Submit" do
it "Submit and expects to succeed" do
allow_any_instance_of(Concerns::Session).to receive(:current_user).and_return(name: "test")
allow_any_instance_of(Concerns::Session).to receive(:user).and_return(name: "test")
post :submit, params
expect(response).to have_http_status(200)
end
end
app/controllers/sample.rb
def submit
list = user.get_list
end
lib/classes/user.rb
class User
def list
return values
end
end
Related
I am new to Rails and I want to test my set strong parameters of the Book model with a controller test. I am using Minitest and Rails 4.
Book model:
class Book < ActiveRecord::Base
validates :title, presence: true, length: { in: 1..150 }
end
Book controller wit params:
def create
#book = Book.new book_params
if #book.save
redirect_to action: "index", notice: 'Success.'
else
render :new
end
end
private
def book_params
params.require(:book).permit(:title, :cover_image)
end
My idea for a test - does fail, because it creates an entry:
assert_no_difference('Book.count') do
post :create, book: {
id: 123,
title: "Lorem ipsum"
}
end
How can I get the tests go green and is it correct to test the strong parameters with a controller test?
I am looking for an answer to almost the same question. When using Rails 5 I eventually came up with a solution (call it workaround if you like :-) for testing that the unwanted params don't actually get through. In my (simplified here) case I want to disallow some "security critical" params being passed through when creating a new user.
In the controller (permitting only email and password):
private
def user_params
params.require(:user).permit(:email, :password)
end
In the integration test:
test "not permitted signup data submitted" do
new_user_email = "tester_" + (0...10).map { ('0'..'9').to_a[rand(26)] }.join + "#testing.net"
get signup_path
assert_difference 'User.count', 1 do
post signup_path, params: { user: { email: new_user_email, password: "testpassword", role_id: 1 } }
end
user = User.last
assert user.email == new_user_email
assert user.role_id == nil
end
Here I submit an additional, "sensitive" parameter role_id with the value of 1 (admin). I expect the user to be created. Then I read that newly (last) created user and expect it to have role_id empty (nil). To make the test fail I add :role_id to user_params. Removing it, makes the test pass. Obviously if your attribute can't be nil (aka NULL in SQL), you can test for default value being stored instead of the submitted one.
Since Rails drops all unpermitted parameters not in permit, the new record will be created, hence the test will be red.
Although, one can raise an exception with the action_on_unpermitted_parameters method when non-whitlisted parameters are submitted.
I do like to test Strong Parameters in the controller. I also like to test them more directly, so this is how I do it.
First, I have a test helper that is required in my test/test_helper.rb file:
test/test_helpers/controller_strong_params_helper.rb
# frozen_string_literal: true
module ControllerStrongParamsHelper
def assert_requires_param(param, &block)
#controller.params = ActionController::Parameters.new()
assert_raises(ActionController::ParameterMissing) { yield }
#controller.params = ActionController::Parameters.new(stub_parameter: {})
assert_raises(ActionController::ParameterMissing) { yield }
# It's not enough to have an empty required parameter, there needs to be something inside.
#controller.params = ActionController::Parameters.new(param => {})
assert_raises(ActionController::ParameterMissing) { yield }
#controller.params = ActionController::Parameters.new(param => '')
assert_raises(ActionController::ParameterMissing) { yield }
#controller.params = ActionController::Parameters.new(param => {something_inside: 'something'})
assert_nothing_raised { yield }
end
end
This lets me easily test the strong params that are not optional.
Now assume I have these strong params in my ExampleController:
def example_params
params.require(:example).permit(:id,
:name,
:description)
end
private :example_params
This is what my minitest tests would look like:
test/controllers/example_controller_test.rb
###############################################
test '#example_params should require an example parameter' do
assert_requires_param(:example) { #controller.send(:example_params) }
end
###############################################
test '#example_params should permit some expected example parameters' do
# Using hash rockets so the equality check works.
expected_permitted_params = { 'id' => nil,
'name' => nil,
'description' => nil }
# Specifically merge in any potential non-attribute parameters here if present/needed.
all_params = { example: Example.new.attributes }
#controller.params = ActionController::Parameters.new(all_params)
actual_permitted_params = #controller.send(:example_params)
assert_equal(expected_permitted_params, actual_permitted_params)
end
I have a Account.create! method that creates multiple other models.
In my RSpec test how can I test if the other models were created correctly?
it "should create an account" do
params = ....
Account.create!(params)
expect(account.valid?).to eq(true)
end
My Account.create! method looks like:
def self.create!(params)
account = Account.new(params)
user = ...
user.save!
location = ...
location.save!
account
end
For one thing, you can test that the records were created at all.
it "should create a user" do
params = ....
expect(Account.create!(params)).to change{User.count}.by(1)
end
Also, you can use User.last to retrieve the last user created and check the attributes are what you would want to see.
I have a decorator that receives the application controller as a variable in order to access session variables. Something like:
navigation = NavigationDecorator(user_id, self)
Self being the ApplicationController.
Everything works fine, but now I have to test it and in Rspec I did
navigation = NavigationDecorator(user_id, ApplicationController.new)
During my tests I get:
ActionController::Metal#session delegated to #_request.session, but #_request is nil: #<ApplicationController:0x000000161363f0 #_routes=nil, #_action_has_layout=true, #_headers={"Content-Type"=>"text/html"}, #_status=200, #_request=nil, #_response=nil>
Updating:
I use it like this:
def initialize(user, controller)
#controller = controller
...
end
def retrieve_user_id
user_id = #controller.session[:temporary_id] if #controller.session[:temporary_id]
super
end
I'd propose to stub controller in the test, because since you're writing unit test (you're writing unit test, right?) you want to isolate your system under test (NavigationDecorator) from its dependencies (controller). You can write this test:
describe NavigationDecorator do
context 'user_id'
it 'should take id from session' do
session = { temporary_id: 'temporary' }
controller = instance_double('ApplicationController', session: session)
user = instance_double('User', id: 'user_id')
subject = described_class.new(user, session)
expect(subject.retrieve_user_id).to eq session[:temporary_id]
end
end
end
The test shows us that we have unneeded dependency (controller) and it would be cleaner to pass session right away (if you can, of course):
describe NavigationDecorator do
context 'user_id'
it 'should take id from session' do
session = { temporary_id: 'temporary' }
subject = described_class.new('local id', session)
expect(subject.retrieve_user_id).to eq session[:temporary_id]
end
it 'should take id from user when session is empty' do
controller = instance_double('ApplicationController', session: {})
user = instance_double('User', id: 'user_id')
subject = described_class.new(user, session)
expect(subject.retrieve_user_id).to eq user.id
end
end
end
I have a controller create action that creates a new blog post, and runs an additional method if the post saves successfully.
I have a separate factory girl file with the params for the post I want to make. FactoryGirl.create calls the ruby create method, not the create action in my controller.
How can I call the create action from the controller in my RSpec? And how would I send it the params in my factory girl factories.rb file?
posts_controller.rb
def create
#post = Post.new(params[:post])
if #post.save
#post.my_special_method
redirect_to root_path
else
redirect_to new_path
end
end
spec/requests/post_pages_spec.rb
it "should successfully run my special method" do
#post = FactoryGirl.create(:post)
#post.user.different_models.count.should == 1
end
post.rb
def my_special_method
user = self.user
special_post = Post.where("group_id IN (?) AND user_id IN (?)", 1, user.id)
if special_post.count == 10
DifferentModel.create(user_id: user.id, foo_id: foobar.id)
end
end
end
Request specs are integration tests, using something like Capybara to visit pages as a user might and perform actions. You wouldn't test a create action from a request spec at all. You'd visit the new item path, fill in the form, hit the Submit button, and then confirm that an object was created. Take a look at the Railscast on request specs for a great example.
If you want to test the create action, use a controller spec. Incorporating FactoryGirl, that would look like this:
it "creates a post" do
post_attributes = FactoryGirl.attributes_for(:post)
post :create, post: post_attributes
response.should redirect_to(root_path)
Post.last.some_attribute.should == post_attributes[:some_attribute]
# more lines like above, or just remove `:id` from
# `Post.last.attributes` and compare the hashes.
end
it "displays new on create failure" do
post :create, post: { some_attribute: "some value that doesn't save" }
response.should redirect_to(new_post_path)
flash[:error].should include("some error message")
end
These are the only tests you really need related to creation. In your specific example, I'd add a third test (again, controller test) to ensure that the appropriate DifferentModel record is created.
I'm testing to make sure that a created user is assigned to my instance variable #user. I understand what get means, but I'm not sure what to write for the test. I'm returning with an argument error for a bad URI or URL. What's wrong with my test and how do I fix it?
it "checks #user variable assignment for creation" do
p = FactoryGirl.create(:user)
get :users
# I'm confused on what this line above means/does. What does the hash :users refer
#to
assigns[:user].should == [p]
end
The expected URI object or string error refers to get :users and the error is as follows
Failure/Error get :users
ArgumentError:
bad argument: (expected URI object or URI string)
I guess that what you want is
it "checks #user variable assignment for creation" do
p = FactoryGirl.create(:user)
get :show, id: p.id
assigns(:user).should == p
end
The line you were not sure about checks that content of the assigned variable (#user) in the show view of the user p, is equal to the p user you just created more information there
what action are you trying to test? usually, for creation, you need to test that the controller's "create" action creates a user and assigns an #user variable
I would test it this way:
describe 'POST create' do
it 'creates a user' do
params = {:user => {:name => 'xxx', :lastname => 'yyy'}}
User.should_receive(:create).with(params)
post :create
end
it 'assigns the user to an #user instance variable' do
user = mock(:user)
User.stub!(:create => user)
post :create
assigns(:user).should == user
end
end
notice that I stub/mock all user methods, since you are testing a controller you don't have to really create the user, you only test that the controller calls the desired method, the user creation is tested inside the User model spec
also, I made 2 tests (you should test only 1 thing on each it block if possible, first it test that the controller creates a user, then I test that the controller assigns the variable
I'm assuming your controller is something like this:
controller...
def create
#user = User.create(params[:user])
end
which is TOO simple, I guess you have more code and you should test that code too (validations, redirects, flash messages, etc)