Why is my format validation failing for permalinks? - ruby-on-rails

I have written an rspec test for checking invalid characters in my permalink:
describe "formatting permalinks when creating a page" do
it "does not allow crazy characters" do
page = create(:page, permalink: '#$%^&*first-title')
expect(page).to have(1).errors_on(:permalink)
end
end
In my page.rb model, I have this validation implemented to make it pass:
class Page < ActiveRecord::Base
validates :permalink, format: {:with => /\A[a-zA-Z-]+\Z/, :on => :save!}
before_create :create_slug
def create_slug
self.permalink = self.permalink.parameterize
end
end
But I get his error:
expected 1 errors on :permalink, got 0
What am I doing wrong? How do I fix this?

Your create_slug calls parameterize. Because it's run as a before_create, it changes '#$%^&*first-title' to "first-title".
Also, according to the docs, on: should only be used with create and update, so I'm not sure this is running at all.

Related

Request Spec failing on Association - Rails 5 API

I've got a request spec failing on a model with an association; when I binding.pry into it I can manually create the record with the same parameters, but when I pass them through the post '/path/model' request, it fails and says the association should exist. That's good - I want the association to be required. But the post action seems to be unable to pass.
# job model
belongs_to :worker
validates :title, presence: true
# worker model
has_many :jobs
# jobs controller
# POST /jobs
def create
#job = Job.create!(job_params)
json_response(#job, :created)
end
# jobs request spec
describe "POST /v1/jobs" do
context "when the request is valid" do
before {
post '/v1/jobs', params: {
title: "Whatever",
worker_id: Worker.first.id,
}
}
it "creates a job" do
puts request.body.read
puts response.body
expect(json["title"]).to eq("Whatever")
end
it "returns status code 201" do
expect(response).to have_http_status(201)
end
end
end
These tests both fail. The result of the puts statements above is:
title=Whatever&worker_id=21
{"message":"Validation failed: Worker must exist"}
If I put a binding.pry there instead, the following successfully creates a Job:
Job.create(title: "Whatever", worker_id: Worker.first.id)
My migrations are:
# jobs migration
create_table :jobs do |t|
t.references :worker, index: true
t.text :title
end
# worker migration
create_table :workers do |t|
t.text :first_name
...
end
What am I missing here? I know that the belongs_to association is now non-optional in Rails 5, but again, I want that. So I don't want to flag it optional just to get my tests to pass. Any ideas?
It looks like you haven't created a Worker for the spec to work with. Remember that your database is empty in each spec, so Worker.first.id will fail, because there is no workers in the database.
In your spec you want to create a worker (I use FactoryBot, the same concept applies to fixtures or whatever you're using):
context "when the request is valid" do
let!(:worker) { create :worker }
before {
post '/v1/jobs', params: {
title: "Whatever",
worker_id: worker.id,
}
}
it "creates a job" do
puts request.body.read
puts response.body
expect(json["title"]).to eq("Whatever")
end
it "returns status code 201" do
expect(response).to have_http_status(201)
end
end
I used let! because that will force the record to be created before the spec is run. Now Worker.first.id will find that worker, and you're on your way!
You're correct in that it's caused by belongs_to associations being required by default in Rails 5.
What I usually do is make it optional for creates: belongs_to :worker, optional: :new_record? and then write a test to ensure that it did get created.
Turns out this was a problem in the controller; I'm using strict parameters and didn't have the worker_id in the list of permitted params. Easy to overlook but it's a trap! Hope this helps someone else, especially since all the advice about the belongs_to association for Rails 5 says, "just make it optional."

rails 5 validators not protecting against a missing attribute during Model create?

cannot seem to get my validators to work to ensure all attributes are present to allow a User to be created. Basic User with 2 attributes
class User < ApplicationRecord
validates :name, presence: true
validates :email, presence: true
end
tests to check that name and email are present when created. these #pass
RSpec.describe User, type: :model do
context 'validations' do
subject { FactoryGirl.build(:user) }
it { is_expected.to validate_presence_of(:email) }
it { is_expected.to validate_presence_of(:name) }
it "fails to create user unless both are present" do
expect { User.create(:name => 'jo bloggs1', :noemail => 'c#c.co')}.to raise_error(ActiveModel::UnknownAttributeError)
end
end
end
but if i try and create model with a missing attribute no error is raised
it "fails to create user unless both are present" do
expect { User.create(:name => 'jo bloggs1')}.to raise_error(ActiveModel::MissingAttributeError)
end
result
1) User validations fails to create user unless both are present
Failure/Error: expect { User.create(:name => 'jo bloggs1')}.to raise_error(ActiveModel::MissingAttributeError)
expected ActiveModel::MissingAttributeError but nothing was raised
# ./spec/models/user_spec.rb:12:in `block (3 levels) in <top (required)>'
fyi, FactoryGirl
FactoryGirl.define do
factory :user do
name "MyString"
email "MyString"
end
end
i have tried clever stuff like
class User < ApplicationRecord
# before_create :run_it
after_initialize :all_present?
validates :name, presence: true
validates :email, presence: true
private
def all_present?
if (#email.nil? || #name.nil?)
raise ActiveModel::MissingAttributeError.new()
end
end
end
but cannot seem to raise these manually...?
what am i doing wrong?
tx all
Ben
The problem is that there are 2 methods, create and create!. The first, create
The resulting object is returned whether the object was saved successfully to the database or not
Whereas with create!:
Raises a RecordInvalid error if validations fail, unlike Base#create
So, create fails silently and doesn't raise any exceptions, but you can still inspect the instance and see that it's a new record and has errors and such, and create! fails noisily, by raising the error you are expecting it to raise. In short, your test should be:
it "fails to create user unless both are present" do
expect { User.create!(:name => 'jo bloggs1')}.to raise_error(ActiveModel::MissingAttributeError)
end

Proper way of resetting sequence in FactoryGirl before every tests

I am using FactoryGirl and populate unique attribute whenever the model is made. The problem with my model form is that there are only 4 different types available for form_type attribute. So I need to reset the sequence everytime I run tests. Like below, I user before do block to call FactoryGirl.reload. However, I saw an article saying it is anti-pattern to FactoryGirl. What is the best way to reset the sequence in FactoryGirl instead of calling FactoryGirl.reload before every test?
Here is my forms.rb Factorygirl file,
FactoryGirl.define do
factory :form do
association :user
sequence :form_type do |n|
Form.form_types.values[n]
end
end
end
Here is my form.rb model file:
class Form < ActiveRecord::Base
belongs_to :user, required: true
enum form_types: { :a => "Form A", :b => "Form B", :c => "Form C", :d => "Form D"}
validates :form_type, presence: true
validates :form_type, uniqueness: {scope: :user_id}
end
Here is my forms_controller_spec.rb file:
require 'rails_helper'
RSpec.describe FormsController, type: :controller do
login_user
let(:form) {
FactoryGirl.create(:form, user: #current_user)
}
let(:forms) {
FactoryGirl.create_list(:form , 3, user: #current_user)
}
let(:form_attributes) {
FactoryGirl.attributes_for(:form, user: #current_user)
}
describe "GET #index" do
before do
FactoryGirl.reload
end
it "loads all of the forms into #forms" do
get :index
expect(assigns(:forms)).to match_array(#forms)
end
end
end
Hm, it seems like the purpose of FG sequences is to ensure unique numbers. Or at least that's what I've used it for. You may be able to hack into FG if this is what you really want.
This question may help.
How can I reset a factory_girl sequence?

post:create in rspec test giving 'count should have been changed by 1, but was changed by 0'

I've got a problem with testing a create method of a controller. My test:
describe "POST #create" do
it "creates a new admin_user" do
expect{
post :create, :admin_user => FactoryGirl.attributes_for(:admin_user)
}.to change(Admin::User,:count).by(1)
end
end
And the failed spec I'm getting:
1) Admin::UsersController logged in POST #create creates a new admin_user
Failure/Error: expect{
count should have been changed by 1, but was changed by 0
# ./spec/controllers/admin_users_controller_spec.rb:75:in `block (4 levels) in <top (required)>'
Here's my controller:
def create
#admin_user = Admin::User.new(params[:admin_user])
if #admin_user.save
render nothing: true
end
end
and a factory:
require "factory_girl"
require 'ffaker'
FactoryGirl.define do
factory :admin_user, class: "Admin::User" do |f|
f.name {Faker::Name.first_name}
f.email {Faker::Internet.email}
f.password "aaa"
end
end
and my model:
class Admin::User < ActiveRecord::Base
has_secure_password
validates :email, :uniqueness => true, :presence => true
validates_presence_of :password, :on => :create
end
I have no idea what might be wrong. I've searched the whole internet for it but didn't find the answer. Adding user from the rails console works just fine. Any help would be highly appreciated. Thanks in advance.
This line:
post :create, FactoryGirl.attributes_for(:admin_user)
Should be this:
post :create, :admin_user => FactoryGirl.attributes_for(:admin_user)
As an exercise, print out the params (p params or puts params.inspect) in your create action in your controller and you'll see the difference.
EDIT You should try 2 things:
Still print out the param to see if they make sense to you.
The problem might be that your params just aren't valid. Instead of using save, try using save! which will throw an error if any of your validations are wrong, and you'll see the error in your test output. You should handle the situation if the save fails anyways.

validates_uniqueness_of don't work

It's not some kind of synchronization problem I readed before.
The code is quite simple.
The model:
class User < ActiveRecord::Base
attr_accessor :name, :email
validates_uniqueness_of :email, :on => :create, :message => "must be unique"
end
The rspec test:
require 'spec_helper'
describe User do
before(:each) do
#valid_attributes = {
:name => "Foo Bar",
:email => "foo#bar.com"
}
end
it "should reject duplcate email address" do
User.create!(#valid_attributes)
duplicate_user = User.new(#valid_attributes)
duplicate_user.should_not be_valid
end
end
I run the test, and get error message:
----------------------------
1)
'User should reject duplcate email address' FAILED
expected #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> not to be valid
/Users/mac/workspace/rails_space/uniq/spec/models/user_spec.rb:14:
Finished in 0.067908 seconds
1 example, 1 failure
-----------------------------
I run the script/console, and create two user objects with same email address. It goes fine, no validate message occur, the two objects both have inserted into the table. I don't what's wrong with it.
My rails version is 2.3.8 and rspc is 1.3.0.
I believe the problem is the attr_accessor line that you have. If you have those column names, the accessor will override the column name and that is just part of the class and doesn't care about uniqueness. If you are going to have the accessor methods then it needs to get back to the database in some way. If you need to have the accessor, then you need to tie it to the database by calling write_attribute.
For more information you can see the documentation for "Overwriting default accessors" at http://api.rubyonrails.org/classes/ActiveRecord/Base.html
I hope this helps!
I think the issue is because you are saying:
validates_uniqueness_of :email, :on => :create
User.new may not be triggering this validation.
Try calling duplicate_user.save! and see if that throws an error.
You can try like following
attr_accessible :email
validates_uniqueness_of :email, :on => :create, :message => "must be unique"

Resources