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
Related
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.
i have been even trying pending on this but it still gives error.
Userfactory
FactoryGirl.define do
sequence :email do |n|
"email#{n}#evercam.io"
end
factory :user, class: :EvercamUser do
sequence(:firstname) { |n| "firstname#{n}" }
sequence(:lastname) { |n| "lastname#{n}" }
sequence(:username) { |n| "username#{n}" }
sequence(:password) { |n| "password#{n}" }
email
sequence(:api_id) {|n| SecureRandom.hex(10)}
sequence(:api_key) {|n| SecureRandom.hex(16)}
# is_admin false
country do
country = Country.where(iso3166_a2: 'ie').first
country || create(:ireland)
end
end
end
UserSpec
require 'rails_helper'
RSpec.describe User, type: :model do
describe 'validations' do
it { should validate_presence_of :email }
it { should validate_presence_of :firstname }
it { should validate_presence_of :lastname }
it { should validate_presence_of :username }
it { should validate_presence_of :encrypted_password }
describe 'email uniqueness' do
before { create :user, email: 'foo#bar.com' }
let(:user) { build :user, email: 'foo#bar.com' }
it do
user.valid?
expect(user.errors[:email]).to be == ['has already been taken']
end
end
end
describe 'associations' do
it { should belong_to(:country) }
# it { should have_many(:camera_shares) }
end
it 'has a valid factory' do
expect(build(:user)).to be_valid
end
end
and here is my UserModel which is Devise
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :country
validates :firstname, presence: true
validates :lastname, presence: true
validates :username, presence: true
validates :encrypted_password, presence: true
def fullname
"#{firstname} #{lastname}"
end
def self.created_months_ago(number)
given_date = number.months.ago
User.where(created_at: given_date.beginning_of_month..given_date.end_of_month)
end
end
I have even tried to give "pending" to all blocks but it still giving me error i dont know where i am wrong on this
Any help will be appreciated thanks
it { should validate_presence_of :email }
If it fails on the line above, it is because the model does not in fact have a validation for :email:
validates :firstname, presence: true
validates :lastname, presence: true
validates :username, presence: true
validates :encrypted_password, presence: true
# validates :email, presence: true # <---- missing in action
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
I'm working through an API for Rails and have been fumbling how to set up the associations, the spec, and the controller for my Get route. The goal - As a user I want to get all the notes closest to my location that have not been viewed. I know the viewed? logic is off, as is the Query Interface in the Recipients Model and Nearests Controller.
Here's the Error Rspec is giving me:
Failure/Error: note1 = create(:note)
NoMethodError:
undefined method `recipient_id=' for #<Note:0x007fd2a40e1400>
Here's the spec:
describe 'GET /v1/notes/nearests?lat=&lon=&radius=' do
it 'returns the notes within the given radius' do
near_note1 = create(:note, lat: 37.760322, lon: -122.429667)
near_note2 = create(:note, lat: 37.760322, lon: -122.429667)
lat = 37.771098
lon = -122.430782
radius = 10
get "/v1/notes/nearests?lat=#{lat}&lon=#{lon}&radius=#{radius}"
expect(response_json).to eq([
{
'id' => [near_note1.id, near.note2.id],
# 'lat' => near_note1.lat,
# 'lon' => near_note1.lon,
'note_text' => [near_note1.note_text, near_note2.note_text],
'photo_uri' => [near_note1.photo_uri, near_note2.photo.uri],
# 'expiration' => near_note.expiration.as_json,
'viewed' => [near_note1.viewed?, near_note2.viewed?]
},
])
end
end
Here is the controller code:
def index
#notes = Note.near([
params[:recipient_id],
params[:lat],
params[:lon]],
radius: :APP_CONFIG['radius'],
units: :APP_CONFIG['units']
)
end
Here are the Factories - Notes
FactoryGirl.define do
factory :note do |u|
sender_id {FactoryGirl.create(:user).id}
recipient_id {FactoryGirl.create(:user).id}
lat 1.5
lon 1.5
note_text "MyString"
photo_uri "MyString"
expiration Time.zone.now.utc
end
end
My Models:
User Model
class User < ActiveRecord::Base
has_many :notes
validates :first_name, :last_name, :pw, presence: true
validates :email, :username, :devicetoken, presence: true, uniqueness: true
validates :email, length: { minimum: 8 }
end
Note Model
class Note < ActiveRecord::Base
belongs_to :user, foreign_key: 'sender_id', class_name: 'User'
has_many :recipients, foreign_key: 'recipient_id', class_name: 'User'
validates :sender_id, presence: true
validates :lat, presence: true
validates :lon, presence: true
validates :note_text, presence:true
validates :expiration, presence: true
reverse_geocoded_by :lat, :lon
end
Recipients Model
class Recipient < ActiveRecord::Base
belongs_to :note, foreign_key: 'recipient_id', class_name: 'Note'
def get_recipient
Note.find(:all, params[:note_id])
end
def viewed?
end
end
Fixed. There was a bug in Rails. See https://github.com/rails/rails/issues/2333
I have a problem with Factory Girl Rails and Rails 3.1.0.rc5
When I do more than once user = FactoryGirl.create(:user) I have an error.
Failure/Error: user = FactoryGirl.create(:user)
NameError:
uninitialized constant User::User
# ./app/models/user.rb:17:in `generate_token'
# ./app/models/user.rb:4:in `block in <class:User>'
# ./spec/requests/users_spec.rb:20:in `block (3 levels) in <top (required)>'
I can create as many user as I want using Factory but only in rails console.
Tests:
require 'spec_helper'
describe "Users" do
describe "signin" do
it "should sign in a user" do
visit root_path
user = FactoryGirl.create(:user)
within("div#sign_in_form") do
fill_in "Name", with: user.name
fill_in "Password", with: user.password
end
click_button "Sign in"
current_path.should eq(user_path(user))
page.should have_content("signed in")
end
it "should not show new user form on /" do
user = FactoryGirl.create(:user)
visit root_path
page.should_not have_css("div#new_user_form")
end
end
end
factories.rb
FactoryGirl.define do
factory :user do |f|
f.sequence(:name) { |n| "john#{n}" }
f.fullname 'Doe'
f.sequence(:email) { |n| "test#{n}#example.com" }
f.password 'foobar'
end
end
model/user.rb
class User < ActiveRecord::Base
has_secure_password
attr_accessible :name, :fullname, :email, :password
before_create { generate_token(:auth_token) }
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, presence: true, length: { maximum: 20 },
uniqueness: { case_sensitive: false }
validates :fullname, presence: true, length: { maximum: 30 }
validates :email, format: { with: email_regex },
uniqueness: { case_sensitive: false }, length: { maximum: 30 }
validates :password, length: { in: 5..25 }
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
User.exists?(column => self[column]) causes the problem.
Somehow the class is not properly looked up, and I am not sure how this is happenning but could you try accessing it differently:
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while self.class.exists?(column => self[column])
end
You've got an extra line i your factories.rb, it should read like this:
FactoryGirl.define :user do |f|
f.sequence(:name) { |n| "john#{n}" }
f.fullname 'Doe'
f.sequence(:email) { |n| "test#{n}#example.com" }
f.password 'foobar'
end
This should work:
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "john#{n}" }
fullname 'Doe'
sequence(:email) { |n| "test#{n}#example.com" }
password 'foobar'
end
end