I learned while actually writing RSpec from a state where I had never written a test. I would like to leave what I understand in the article along with the code that actually describes it.
--Users table
--Columns: ```email: stringand
password: stringonly --One-to-many association with Books table --Use Gem
FactoryBotto generate test data --Use easy-to-write Gem
Shoulda Matchers`` for testing
With Shoulda Matchers
, you can write a test in one line that you couldn't do without writing many lines. There are some parts that you do not know what is being done, but there are useful syntaxes other than the contents described this time, so if you are interested, I recommend you to check it from the reference site. ..
-[Introduction to RSpec that can be used, Part 1 "Understanding the basic syntax and useful functions of RSpec"](https://qiita.com/jnchito/items/42193d066bd61c740612#let-%E3%81%AE%E3% 83% A1% E3% 83% AA% E3% 83% 83% E3% 83% 88% E3% 82% 92% E6% B4% BB% E3% 81% 8B% E3% 81% 97% E3% 81% A6-age-% E3% 82% 82-let-% E3% 81% A7% E7% BD% AE% E3% 81% 8D% E6% 8F% 9B% E3% 81% 88% E3% 82% 8B) -Introduction to RSpec that can be used, part 2 "Mastering frequently used matchers"
It was very easy to understand and was very helpful! Thank you very much!
Model
app/models/user.rb
class User < ApplicationRecord
has_many :books #One-to-many association with books
has_secure_password
before_save { email.downcase! }
validates :email, presence: true,
length: { maximum: 255 },
format: { with: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i },
uniqueness: { case_sensitive: false }
validates :password, presence: true,
length: { minimum: 6 }
end
We are doing a total of 9 tests
spec/models/user_spec.rb
# rails_Read helper file
require 'rails_helper'
#User model test, so User, type: :As a model
RSpec.describe User, type: :model do
describe 'has_many' do
# has_many :Testing books
it { should have_many(:books) }
end
describe 'has_secure_password' do
# has_secure_password test
it { should have_secure_password }
end
describe 'validation' do
context 'email' do
#Create test data with FactoryBot
let(:user) { create(:user) }
let(:upcase_email_user) { build(:user, :upcase_email) }
# presence:True test
it { should validate_presence_of(:email) }
# length: { maximum: 255 }Test of
it { should validate_length_of(:email).is_at_most(255) }
# uniqueness: { case_sensitive: false }Test of
it 'Do not allow duplicate email storage' do
duplicate_user = user.dup
duplicate_user.email = user.email.upcase
expect(duplicate_user).to be_invalid
end
# format: { with: XXXXX }Test of
it 'Do not allow emails that do not fit the specified format' do
invalid_emails = %w[user@foo,com user_at_foo.org [email protected]@bar_baz.com foo@bar+baz.com [email protected]]
invalid_emails.each do |invalid_email|
user.email = invalid_email
expect(user).to be_invalid
end
end
# before_save { email.downcase! }Test of
it 'downcase!Is working properly' do
upcase_email_user.save!
expect(upcase_email_user.email).to eq '[email protected]'
end
end
context 'password' do
# presence:True test
it { should validate_presence_of(:password) }
# length: { minimum: 6 }Test of
it { should validate_length_of(:password).is_at_least(6) }
end
end
end
RSpec.describe model name, type :: test format do
, and you can test the User model in the block with this description.
RSpec.describe User, type: :model do
Validate model association and application of has_secure_password
using Shoulda Matchers
# has_many :Testing books
describe 'has_many' do
it { should have_many(:books) }
end
# has_secure_password test
describe 'has_secure_password' do
it { should have_secure_password }
end
describe 'validation' do
context 'email' do
#Write email validation test
end
context 'password' do
#Write password validation test
end
end
Define the test data created by the factory bot in a dedicated file
spec/factories/users.rb
FactoryBot.define do
factory :user do
#Every time data is generated, a serial number is assigned to create a unique value.
sequence(:email) { |n| "test#{n}@test.com" }
password { 'password' }
trait :upcase_email do
email { '[email protected]' }
end
end
end
Test data is created with let
based on the above file, and this test data can be used in the ʻit .. do ~~ end`` block in the described block. In the comment out, the result of checking the contents of
ʻuser and` ʻupcase_email_user
in binding.pry
is described.
let(:user) { create(:user) }
# user = <User:0x0000560469431138 id: 2610, email: "[email protected]", password_digest: "[FILTERED]", created_at: Tue, 27 Oct 2020 14:03:04 JST +09:00, updated_at: Tue, 27 Oct 2020 14:03:04 JST +09:00>
let(:upcase_email_user) { build(:user, :upcase_email) }
# upcase_email_user = <User:0x00005604690c12c8 id: nil, email: "[email protected]", password_digest: "[FILTERED]", created_at: nil, updated_at: nil>
create
and build
apply the method that can omit the description of FactoryBot
.Write tests using Shoulda Matchers
# presence:Test true
it { should validate_presence_of(:email) }
# length: { maximum: 255 }Test
it { should validate_length_of(:email).is_at_most(255) }
Create copy data of ʻuser`` with `` dup method``. Verify that `` duplicate_user`` with the same
ʻemail as the already registered` ʻuser
is not saved with let (: user) {create (: user)}
.
By capitalizing the email with `ʻupcase`` before the test, it is also verified that the email is judged to be the same even if it is capitalized.
be_invalid
will pass if it is scratched by validation (if an error occurs)
it 'Do not allow duplicate email storage' do
duplicate_user = user.dup
#Contents at this point
# duplicate_user = <User:0x000055f1e1d71e90 id: nil, email: "[email protected]", password_digest: "[FILTERED]", created_at: nil, updated_at: nil>
duplicate_user.email = user.email.upcase
# user.email.Contents after upcase
# #<User:0x000055f1e1d71e90 id: nil, email: "[email protected]", password_digest: "[FILTERED]", created_at: nil, updated_at: nil>
expect(duplicate_user).to be_invalid # duplicate_user.invalid?Pass if is true
end
it 'Do not allow emails that do not fit the specified format' do
#Prepare 6 arrays of character strings that do not match the specified format
invalid_emails = %w[user@foo,com user_at_foo.org [email protected]@bar_baz.com foo@bar+baz.com [email protected]]
#Verify by putting an email that does not match the format in the user's email
invalid_emails.each do |invalid_email|
user.email = invalid_email
expect(user).to be_invalid # duplicate_user.invalid?Pass if is true
end
end
It is verified that the email [email protected]
of ```upcase_email_userbecomes
[email protected] after save. `ʻexpect (X) .to eq Y
tests" X is equal to Y ".
it 'downcase!Is working properly' do
# before_save is called just before save, so save!To do
upcase_email_user.save!
expect(upcase_email_user.email).to eq '[email protected]'
end
Write tests using Should a Matchers
as well as email
context 'password' do
# presence:True test
it { should validate_presence_of(:password) }
# length: { maximum: 6 }Test of
it { should validate_length_of(:password).is_at_least(6) }
end
At the beginning of learning RSpec, I was not familiar with the syntax and had a lot of trouble. However, there are many articles with easy-to-understand explanations, and it will be fun if you get used to it and pass the test as you expected. I wrote an article about model testing this time, but I also learned about request testing, so I hope I can write an article about it in the future.
Recommended Posts