My user#update method appears to be working as anticipated. When performing this method as requested from my front-end, a successful update occurs. The test for the method, however, fails.
For my test, I'm logging the user in, then attaching the returned auth_token in the request headers.
Here is the test:
test "should successfully update valid email" do
old = #user.auth_token
post api_login_url, params: { session: #credentials }
#user.reload
assert_not_equal old, #user.auth_token
old_user = #user
#request.headers["Authorization"] = #user.auth_token
patch "/api/users/" + #user.id.to_s, params: { email: "new#me.com", password: "testtest" }, headers: #request.headers
assert_response 201
assert_equal "new#me.com", User.find(#user.id).email
end
The error:
Expected: "new#me.com"
Actual: "test#woohoo.com"
In my controller (#user is already set):
before_action :authenticate_with_token!, only: [:update, :destroy]
before_action :set_user, only: [:show, :update, :destroy, :confirm, :posts, :comments]
wrap_parameters :user, include: [:username, :email, :password, :password_confirmation]
def update
if #user.update(user_params)
render json: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
def user_params
params.require(:user).permit(:username, :email, :password, :password_confirmation, :confirmation_code, :confirmed)
end
The user model:
require 'json_web_token'
class User < ApplicationRecord
before_save { email.downcase! }
before_create :generate_authentication_token!
before_update :reset_confirmed!, :if => :email_changed?
has_secure_password
has_many :posts
has_many :comments
has_many :votes
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
validates :username, presence: true, length: { maximum: 24 }, uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 8 }
validates :auth_token, uniqueness: true
def generate_authentication_token!
begin
self.auth_token = JsonWebToken.encode('id' => self.id, 'username' => self.username)
end while self.class.exists?(auth_token: auth_token)
end
def destroy_token!
self.auth_token = nil
end
def reset_confirmed!
self.confirmed = false
end
end
In case anyone will ask, I've tried removing the before_update :reset_confirmed! call.
I've tried rewriting the tests many different ways. The assert_response 201 does not fail.
Rails version: 5.0.0.beta2
Ruby version: 2.2.3 (x86_64-darwin15)
You need to wrap your params:
patch "/api/users/" + #user.id.to_s,
params: { user: { email: "new#me.com", password: "testtest" }},
headers: #request.headers
I am however quite surprised you are not getting an exception (require(:user) should raise one when params[:user] is missing), so let me know if it is working.
Related
I have a problem with my testing in rails with rspec. All in all I have a structure between 4 models, but for the moment I try to solve my testing for two of them. I'm using faker and FactoryGirl and have the following factories:
require 'faker'
FactoryGirl.define do
factory :user do |f|
f.name { Faker::Name}
f.email { Faker::Internet.email}
f.password {Faker::Internet.password}
f.role {"Kindergarten"}
end
end
require 'faker'
FactoryGirl.define do
factory :child do |f|
f.name { Faker::Name}
f.city { Faker::Address.city}
f.postalcode {Faker::Number.between(30000,35000)}
f.streed {Faker::Address.street_name}
f.add_number {Faker::Address.secondary_address}
f.disability { Faker::Boolean.boolean}
f.halal { Faker::Boolean.boolean}
f.koscha {Faker::Boolean.boolean}
f.vegetarian {Faker::Boolean.boolean}
f.vegan {Faker::Boolean.boolean}
f.allday { Faker::Boolean.boolean}
f.gender { Faker::Number.between(0,1)}
f.user_id {Faker::Number.between(1,10)}
end
end
and my controller test looks like that
require 'rails_helper'
require 'factory_girl_rails'
describe UsersController do
before do
3.times { FactoryGirl.create(:child)}
end
describe "GET #show" do
let(:user) { FactoryGirl.create(:user) }
before { get :show, id: user.id}
it "assigns the requested user to #user" do
assigns(:user).should eq user
end
it "renders the :show template"
end
describe "GET #new" do
let(:user) { FactoryGirl.create(:user) }
it "assigns a new User to #user" do
get :new, id: user.id
assigns(:user).should be_a_new(User)
end
it "renders the :new template"
end
end
When I try to run the test, I got this error message
UsersController GET #new assigns a new User to #user
Failure/Error: 3.times { FactoryGirl.create(:child)}
ActiveRecord::RecordInvalid:
Validation failed: User can't be blank
My relations and validations in the models are as follow
class Child < ApplicationRecord
has_many :relations, :dependent => :destroy
accepts_nested_attributes_for :relations
belongs_to :user
validates :user, presence: true
validates :name, presence: true, length: { maximum: 50 }
validates :city, presence: true, :on => :create
validates :postalcode, presence: true, numericality: true
validates :streed, presence: true
validates :add_number, presence: true
validates :disability, inclusion: { in: [true, false] }
validates :halal, inclusion: { in: [true, false] }
validates :koscha, inclusion: { in: [true, false] }
validates :vegetarian, inclusion: { in: [true, false] }
validates :vegan, inclusion: { in: [true, false] }
validates :allday, inclusion: { in: [true, false] }
validates :gender, presence: true
end
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token, :reset_token
has_many :children, dependent: :destroy
has_many :kindergartens, dependent: :destroy
has_many :relations, dependent: :destroy
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :role, presence: true
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
end
My problem is, that I'm not sure if there is a fault in my rspec code or if I have made a mistake in my validations and relations in my normal models. Can someone help me?
I think that it would be better to post the whole factory for you. And I think you don't need to require faker if you have it in Gemfile.
FactoryGirl.define do
factory :child do
name { Faker::Name}
city { Faker::Address.city}
postalcode {Faker::Number.between(30000,35000)}
streed {Faker::Address.street_name}
add_number {Faker::Address.secondary_address}
disability { Faker::Boolean.boolean}
halal { Faker::Boolean.boolean}
koscha {Faker::Boolean.boolean}
vegetarian {Faker::Boolean.boolean}
vegan {Faker::Boolean.boolean}
allday { Faker::Boolean.boolean}
gender { Faker::Number.between(0,1)}
user
end
end
Inside specs when you need to create a child for the particular user:
let(:current_user) { create :user }
let(:child) { create :child, user: current_user }
Try to change your controller spec:
describe UsersController do
let(:user) { create(:user) }
describe "GET #show" do
before { get :show, params: { id: user.id } }
it "assigns the requested user to #user" do
assigns(:user).should eq user
end
it "renders the :show template"
it { expect(response).to have_http_status 200 }
end
describe "GET #new" do
before { get :new }
it "assigns a new User to #user" do
assigns(:user).should be_a_new(User)
end
it "renders the :new template"
end
end
Following is rails-api only app.
In users_controller.rb
def sign_up
#user = User.new(user_params)
if #user.save
user = {}
user[:name] = #user.name
user[:email] = #user.email
user[:access_token] = #user.auth_token
user[:message] = "Successfully Signed up"
render json: user, status: 200
else
render json: #user.errors, status: :unprocessable_entity
end
end
private
def user_params
params.permit(:name, :email, :password, :password_confirmation)
end
In model/user.rb
attr_accessor :password
before_save :encrypt_password, :generate_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } , uniqueness: { case_sensitive:false }
validates :password, :confirmation => true, presence: true, :length => {:within => 6..20}
validates_presence_of :password_salt, :password_hash
validates_presence_of :auth_token
In test/controllers/users_controller_test.rb
test "should create user" do
assert_difference('User.count') do
post :sign_up, {email: 'user_3#example.com', name: 'user_3', password: 12345678, password_confirmation: 12345678}
end
end
user model attributes are:
name email password_hash password_salt auth_token
The test above is failing b'cos #user.errors shows
<ActiveModel::Errors:0x007fd826117140 #base=#<User id: nil, name: "user_3", email: "user_3#example.com", password_hash: nil, password_salt: nil, auth_token: nil, created_at: nil, updated_at: nil>, #messages={:password_salt=>["can't be blank"], :password_hash=>["can't be blank"], :auth_token=>["can't be blank"]}>
On #user.save before_save :encrypt_password, :generate_token aren't called.
Using gem 'bcrypt' to store the password as hash.
How do I correct this?
Instead of before_save :encrypt_password, :generate_token use before_validation :encrypt_password, :generate_token. Code in encrypt_password and generate_token is not triggered because you validate model data first and since data is not valid there is no need to trigger before_save because record would not be saved anyway
I'm getting the following error
Error Message Image
When trying to save a message object in my MessageController. Here is my controller.
class MessagesController < ApplicationController
def new
#message = Message.new
#user = current_user
end
def create
#message = Message.new(message_params)
#message.user = current_user
#message.user_id = current_user.id
if #message.user_email.nil?
#message.user_email = current_user.email
end
if #message.save
# UserMailer.send_email_to_admin(current_user.email).deliver
else
# redirect_to new_message_path(#message)
end
end
private
def message_params
params.require(:message).permit(:subject, :body, :user_email)
end
end
And here is my model.
class Message < ActiveRecord::Base
belongs_to :user
validates :user_email, presence: true
validates :subject, presence: true, length: { minimum: 5 }
validates :message, presence: true, length: { minimum: 10 }
end
I have no idea why this error is appearing because there are no methods named "message" in my app. Any help would be much appreciated!
In your model you have a validation for the message field:
validates :message, presence: true, length: {minimum: 10}
Is that supposed to be a validation for your body field?:
validates :body, presence: true, length: {minimum: 10}
Before saving the record, the validator is calling the message method on the Message instance(#message) to check its presence and validate it. Since you don't have a column named message but have a validation for it, you will get the NoMethodError.
Based on rails validation docs. I need to validate fullname field only on update
# encoding: utf-8
class User < ActiveRecord::Base
GENDER_MALE = true
GENDER_FEMALE = false
attr_accessor :password_confirm,
:term,
:year, :month, :day,
:captcha
validates :username, presence: {message: "Bạn phải nhập tài khoản"},
uniqueness: {message: 'Tài khoản đã tồn tại'}, :on => :update
# validates :password, presence: {message: "Bạn phải nhập mật khẩu"},
# confirmation: {message: 'Mật khẩu không chính xác'}
# validates :password_confirmation, presence: {message: "Bạn phải nhập xác nhận mật khẩu"}
# validates :fullname, presence: {message: "Bạn phải nhập họ tên"}
# validates :email, presence: {message: "Bạn phải nhập email"},
# uniqueness: {message: "Email đã tồn tại"}
# validates :email, format: {with: /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i, message: "Email không đúng định dạng"},
# unless: "email.blank?"
# validates :term, acceptance: {message: "Bạn phải đồng ý điều khoản"}
# # validates :gender, acceptance: {accept: [0,1], message: "Giới tính không hợp lệ"}
# validate :_birthday_validator
# validate :_captcha_validator
#
# before_save :_encrypt_password
def signup
self.birthday = "#{year.to_s}-#{month.to_s}-#{day.to_s}"
self.save
end
def self.human_attribute_name(attr, option = {})
"" || super
end
protected
def _encrypt_password
self.password = Digest::MD5::hexdigest(password)
end
private
def _birthday_validator
unless year.present? && month.present? && day.present?
errors.add(:birthday, 'Bạn phải nhập ngày sinh')
else
errors.add(:birthday, 'Ngày sinh không hợp lệ') unless Date.valid_date?(year.to_i, month.to_i, day.to_i)
end
end
def _captcha_validator
if !(captcha.nil?)
errors.add(:captcha, "Mã xác nhận không hợp lệ") if captcha == false
end
end
end
As understand, this validation rule only run when I call update method, but I have no idea why this rule run all the time
Can anyone tell me why or I missed somethings?
Ps: Can Rails validates only for user defined method, somethings like
validates :username, presence: true, only: [:my_func]
One way would be to set a virtual attribute which you'll only populate in the signup method:
#app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :should_validate
validates :fullname, presence: true, on: :update, if: "should_validate.present?"
end
This way, you can then assign a value to should_validate only when you use signup:
def signup
self.birthday = "#{year.to_s}-#{month.to_s}-#{day.to_s}"
self.should_validate = true
self.save
end
you can use method like
validate :fullname , on: :update
def fullname
if self.fullname.present?
true
else
false
end
end
This simple validation test is failing:
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
#user = User.new(name: "Example User",
email: "user#example.com",
character_attributes: {callsign: "example"},
password: "foobar",
password_confirmation: "foobar"
)
end
test "should be valid" do
assert #user.valid?, "#{#user.errors.messages}"
end
end
...with this message: character.sociable_id"=>["can't be blank"]
I don't understand why the user creation in UserTest is failing to make a valid User.
Each User has_one :character and each Character belongs_to a User.
The User model:
User.rb:
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
has_one :character, as: :sociable, dependent: :destroy
accepts_nested_attributes_for :character
has_secure_password
before_validation do
self.create_character unless character
end
before_save do
self.email.downcase!
end
before_create :create_activation_digest
validates :name, presence: true,
length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }, allow_blank: true
validates :character, presence: true
.
.
end
The Character model:
Character.rb:
class Character < ActiveRecord::Base
belongs_to :sociable, polymorphic: true
has_many :posts, dependent: :destroy
before_save do
self.callsign.downcase!
end
validates :sociable_id, presence: true
VALID_CALLSIGN_REGEX = /\A[a-z\d\-.\_]+\z/i
validates :callsign, presence: true,
length: { maximum: 20 },
format: { with: VALID_CALLSIGN_REGEX },
uniqueness: { case_sensitive: false }
end
It should be:-
test "should be valid" do
assert #user.valid? , "#{#user.errors.messages}"
end