In my Rails 6 app I'm trying to test controller methods which allow admin users to update user data without providing user passwords. All actions are run in ActiveAdmin.
admin/users.rb
controller do
def update
model = :user
%w[password password_confirmation].each { |p| params[model].delete(p) } if params[model][:password].blank?
super
end
end
Based on this page I tried to write specs:
admin/users_spec.rb
describe 'PUT update' do
let(:user) { create(:user, :random_email) }
let(:valid_attributes) do
ActionController::Parameters.new(
{
user: {
email: 'michael.kelso#example.com',
password: '',
},
},
)
end
before do
put :update, params: { id: user.id, user: valid_attributes }
end
it 'update user' do
expect(user.reload.email).to eq('michael.kelso#example.com')
end
end
end
What should I do to have this test green?
you can still add strong params for activeadmin.
Using method permit_params method like: permit_params :title, :content, :publisher_id
activeadmin strong param docs:
https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
this post is related to this one: https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
I am trying to write a simple test to validate updating an Employee and while it works in practice I wanted to write the test anyway.
RSpec.describe EmployeesController, type: :controller do
before(:each) do
admin_user = FactoryBot.create(
:user,
user_type: 1,
email: "admin#admin.com",
password: "oeifhoi2345tf",
password_confirmation: "oeifhoi2345tf"
)
login_as(admin_user)
#employee = create(:employee)
end
it 'expects employee values to update following update' do
p #employee
put :update, params: {
id: #employee[:id],
employee: { name: "New Name" }
}
#employee.reload
p #employee
expect(#employee.name).to eq("New Name")
end
end
The test fails and #employee.name remains unchanged. I have a feeling the update it not even occurring because I added a print line to my controller and I do not see it in my logs:
def update
p "IN UPDATE"
if #employee.update(employee_params)
redirect_to edit_employee_path(#employee[:id])
else
render :edit
end
end
Is there something I am missing in my put call?
I am trying to create a simple tests on my application with devise. I have a redirect set up if the user is not logged in, but even though I use sign_in before the test I still get redirected. When I manually test this, it works fine.
users.yml
george:
name: George
email: george#example.com
encrypted_password: <%= Devise::Encryptor.digest User, '123456' %>
addresses.yml
georges_home:
title: Home
receiver: George
street: '132 Mitchell St SW'
country: 'US'
city: 'Atlanta'
postal_code: '30303'
phone: '+1 404-524-5665'
state: 'Georgia'
user: george
addresses_controller.rb
class AddressesController < ApplicationController
before_action :login_needed
def index
#addresses = Address.where(user_id: #current_user.id)
end
private
def login_needed
if !user_signed_in?
redirect_to new_user_session_path, alert: 'Please log in.'
end
end
end
addresses_controller_test.rb
require 'test_helper'
class AddressesControllerTest < ActionController::TestCase
include Devise::TestHelpers
setup do
sign_in users(:george)
#address = addresses(:georges_home)
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:addresses)
end
end
I found a piece of code that was dealing with fixtures of a User model with devise and the confirmable option. So my users' fixture should look like this:
users.yml
george:
name: George
email: george#example.com
encrypted_password: <%= Devise::Encryptor.digest User, '123456' %>
confirmed_at: <%= Date.today %>
I have to test the creation of my account. This is my Account.create() in controllers method.
Account.create( account: { name: "#{params[:account][:name]}",
description: "#{params[:description]}" }, user: { email: current_user.name })
I have to test my model method Account.create().
post :create, '/account'
Below is my spec class.
require 'spec_helper'
describe Account do
before(:all) do
end
it "can create an account" do
FactoryGirl.create :account, name: "something", description: "something"
# Account.create()
end
end
I am not sure on how to procced.
FactoryGirl is a tool to use when the methods you want to test need a complicated predefined state. When account creation has no dependencies you could simply do something like:
it "can create an account" do
acc = Account.create(name: 'something', description:'something')
acc.name.should == 'something'
acc.description.should == 'something'
end
Please, take a look at this article, it explains how to test Models.
I your case it should be like this:
it "can create an account" do
FactoryGirl.create(:account, name: "something", description: "something").should be_valid
end
I'm relatively new to programming, Rails, Ruby, Rspec, and the like, so thanks for your help!
My specs were very repetitive, so I wrote some spec helper methods. I can't figure out how to properly use them in my specs. Specifically, I have a users controller with create:
def create
#user = User.new(params[:user])
if #user.save
redirect_to user_path(#user)
else
render :action => :new
end
end
A bit in the spec helper that creates a valid user:
def valid_user_eilif
#test_image = Rails.root + "spec/fixtures/images/seagull.jpg"
#file = Rack::Test::UploadedFile.new(#test_image, "image/jpeg")
user = User.create!(:username => "eilif", :email => "eilif#email.org",
:image => #file, :bio => "Lots of text that I don't want to write",
:signature_quote => "Yet more text.")
user.save!
user
end
And then in my user controller spec:
before (:each) do
post :create, :user => valid_user_eilif
end
it 'should assign user to #user' do
assigns(:user).should eq(User.last)
end
When I run the spec I get the error:
Failure/Error: assigns(:user).should eq(User.last)
expected #<User id: 1, username: "eilif", email: "eilif#email.org", bio: "Lots of text that I don't want to write", signature_quote: "I feel empty.", image_file_name: "seagull.jpg", image_content_type: "image/jpeg", image_file_size: 10475, image_updated_at: "2011-05-10 23:35:55", created_at: "2011-05-10 23:35:56", updated_at: "2011-05-10 23:35:56">
got #<User id: nil, username: nil, email: nil, bio: nil, signature_quote: nil, image_file_name: nil, image_content_type: nil, image_file_size: nil, image_updated_at: nil, created_at: nil, updated_at: nil>
So, I assume I'm incorrectly posting to create, since nothing is created? What's the proper way to do this?
Ideally controller specs shouldn't depend on the model being able to create a row in the database. With such a simple action you can mock out the dependencies:
describe UsersController do
context "on success" do
before(:each) do
#user = mock_model(User,:save=>true)
User.stub(:new) {#user}
post :create, :user => {}
end
it "redirects" do
response.should redirect_to(user_path(#user))
end
it "assigns" do
assigns[:user].should == #user
end
end
context "on failure" do
it "renders 'new'" do
#user = mock_model(User,:save=>false)
User.stub(:new) {#user}
post :create, :user => {}
response.should render_template "users/new"
end
end
end
Notice that the specs don't pass anything in params[:user]. This helps enforce the MVC separation of concerns, whereby the model is responsible for handling the attributes, ie. validating, setting up associations, etc. You can't always keep controllers this 'skinny', but it's a good idea to try.
It looks like the problem is that #user doesn't get refreshed after the save. Try assigns(:user).reload.should eql(User.last).
But there's another slight problem, and that's probably still going to fail. You shouldn't be calling post with :user => valid_user_eilif; you want the attributes from your user record, not the actual user object itself. And you're essentially creating a new user in valid_user_eilif and then making your controller create that object again -- if you have any kind of unique constraints, you're going to get a conflict.
This is a good place to use something like factory_girl and mocks. For an example, take a look at how one of my projects handles controller specs. This example uses factory_girl, Mocha and shoulda. I'll annotate it with comments below:
describe MembersController, "POST create" do
before do
# Factory Girl - builds a record but doesn't save it
#resource = Factory.build(:member)
# Mocha expectation - overrides the default "new" behavior and makes it
# return our resource from above
Member.expects(:new).with({}).returns(#resource)
# Note how we expect it to be called with an empty hash; that's from the
# `:member` parameter to `post` below.
end
context "success" do
before do
post :create, :member => {}
end
# shoulda matchers - check for a flash message and a redirect
it { should set_the_flash.to(/successfully created/) }
it { should redirect_to(member_path(#resource)) }
end
context "failure" do
before do
# Mocha - To test a failing example in the controller, we override the
# default `save` behavior and make it return false, otherwise it would
# be true
#resource.expects(:save).returns(false)
post :create, :member => {}
end
# shoulda matchers - check for no flash message and re-render the form
it { should_not set_the_flash }
it { should render_template(:new) }
end
end