D-15.Rspec 从零开始写测试(三) shoulda-matchers && Distribute Candies

今天简单操作测试Associations

有能力用原生Rspec语法去测任何东西,一定超强的,但是为了快速了解这部分,请gem来帮助快速完成测试。


shoulda-matchers

Github:https://github.com/thoughtbot/shoulda-matchers
官网:https://matchers.shoulda.io/
官网上示范了,有无使用此gem需要写的code有差多少。

安装
Gemfile

group :development, :test do
  gem "shoulda-matchers"
end
#放test即可,但我还是把它跟昨天装的gem放一起。
$ bundle install

其Github上说明,使用rails需在spec/rails_helper.rb底下写入此段程序。我们就先用最简单方式处理。

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end

先测一个最简单的have_many
此文毕竟是只是分享如何使用Rspec,就不对其他新增Class多做处理。并省略其测试档案。不可取的行为

$ rails g model sword --no-test-framework
$ rails db:migrate

spec/models/role_spec.rb改下如下。

require 'rails_helper'

RSpec.describe Role, type: :model do
  let(:role) { create(:role) }

  describe 'associations' do
    it { should have_many(:swords) }
  end

  describe "测试验证功能" do
  end
end

使用了shoulda-matchers就真的只需要简短一句it { should have_many(:swords) }就可以检测关联性是否正确,但还是要说,有先了解过原本的怎麽写会比较好。

理所当然这样子直接检测,只会得到一个红灯,所以我们正确加上关联性吧。
讲个笑话:某人写have_many(:sword),查失败原因找了一个晚上


app/models/role.rb

class Role < ApplicationRecord
  has_many :swords
end

```app/models/sword.rb`

class Sword < ApplicationRecord
  belongs_to :role
end

rspec画面

F......

Failures:

  1) Role associations is expected to have many swords
     Failure/Error: it { should have_many(:swords) }
       Expected Role to have a has_many association called swords (Sword does not have a role_id foreign key.)
  略....

当然错误的,正确应该帮swordtable加上role_id这个栏位,才能正确调动关联性,所以不用担心只有一句短短的it { should have_many(:swords) }会让测试水准下降。

接着完成加入栏位动作吧。

$ rails g migration add_column_swords

#migrate
class AddColumnSwords < ActiveRecord::Migration[6.1]
  def change
    add_column :swords , :role_id, :integer
  end
end

$ rails db:migrate

rspec画面。

.......

Finished in 0.20472 seconds (files took 1.59 seconds to load)
7 examples, 0 failures

It work!!!!


接着秉着先写测试再开发的原则,将我们想要建立的关联性测试先写上吧。
spec/models/role_spec.rb

require 'rails_helper'

RSpec.describe Role, type: :model do
  let(:role) { create(:role) }

  describe 'associations' do
    it { should have_many(:swords) }
    it { should have_many(:items).through(:boxes) }
    it { should belong_to(:user) }
    it { should have_one(:skill) }
  end

  #略
end

belongs_to

第一次使用factory_botshoulda应该这边会卡住一下下,所以先写这个。

$ rails g model user --no-test-framework
$ rails g migration role_add_column

#migrate
class RoleAddColumn < ActiveRecord::Migration[6.1]
  def change
    add_column :roles, :user_id, :integer
  end
end

$ rails db:migrate

User

class User < ApplicationRecord
  has_one :role
end

Rolse

class Role < ApplicationRecord
  belongs_to :user
  #略...
end

关联性建好了,user_id栏位也加了,但是这样跑rspec一定会失败,因为没有建立User物件来让测试知道User是不是真的有Role,所以factory_bot的部分也要改写。

spec/factorise/user.rb

FactoryBot.define do
  factory :user do
  end
end
#这样就可以,因为我没有建立其他栏位。

spec/factories/role.rb

FactoryBot.define do
  factory :role do
    user
    name { Faker::Name.first_name }
    job { Faker::Job.title }
    age { rand(5..130)}
  end
end


#如果只想建立一个factories.rb档案。
FactoryBot.define do
  factory :user do
  end

  factory :role do
    user
    name { Faker::Name.first_name }
    job { Faker::Job.title }
    age { rand(5..130)}
  end
end

rspec

.F.F......

Failures:

  1) Role associations is expected to have many items through boxes
     Failure/Error: it { should have_many(:items).through(:boxes) }
       Expected Role to have a has_many association called items (no association called items)
     # ./spec/models/role_spec.rb:8:in `block (3 levels) in <top (required)>

  2) Role associations is expected to have one skill
     Failure/Error: it { should have_one(:skill) }
       Expected Role to have a has_one association called skill (no association called skill)
     # ./spec/models/role_spec.rb:10:in `block (3 levels) in <top (required)>

可以发现只剩两种关联测试了。


接着还是将我做的事写上,但如果关联性很熟,可以略过了。

多对多

$ rails g model item --no-test-framework
$ rails g model box role:references  item:references --no-test-framework
$ rails db:migrate

Box.Model

class Box < ApplicationRecord
  belongs_to :role
  belongs_to :item
end

Item.Model

class Item < ApplicationRecord
  has_many :boxes
  has_many :roles, through: :boxes
end

Role.Model

class Role < ApplicationRecord
  has_many :boxes
  has_many :items, through: :boxes
end

has_one

$ rails g model skill role_id:integer --no-test-framework
$ rails db:migrate

Skill.Model

class Skill < ApplicationRecord
  belongs_to :role
end

Role.Model

class Role < ApplicationRecord
  has_one :skill
end

rspec

..........

Finished in 0.2414 seconds (files took 1.69 seconds to load)
10 examples, 0 failures

https://ithelp.ithome.com.tw/upload/images/20210915/20135887wir9lSWwMq.png

虽然验证跟关联可能是最简单的,测试中也是Model最好写,但是透过两个方便的gem可以让测试更好写~
shoulda_matchers除了关联,验证与contoller都提供了更便捷的语法。

今日的内容:https://github.com/nauosika/Rspec_test/tree/D15


今天的leetcode575.Distribute Candies
题目连结:https://leetcode.com/problems/distribute-candies/
题目重点:真糖果题,会分享这题只是因为这题题目很可爱。
除了看题目眼睛有点花
糖果有不同类型,1代表1号糖果,2代表2号糖果,以此类推。
爱丽丝能吃到最多的量是糖果们/2,又尽可能想吃到每种不同的糖,所以一定会吃到糖果们.uniq
如果今天糖果们.uniq大於糖果们/2,爱丽丝还是最多只能吃到糖果们/2
如果今天糖果们/2大於糖果们.uniq,爱丽丝又希望吃到糖果们.uniq
好可怜的爱丽丝,但是我们题目解完了。

# @param {Integer[]} candy_type
# @return {Integer}
def distribute_candies(candy_type)
  [candy_type.uniq.size, candy_type.size/2].min
end

爱丽丝,多运动跟好好刷牙,就可以吃所有的糖果了。


<<:  Firebase Firestore

>>:  [火锅吃到饱-3]【士官长野地餐厅(台中) Sauerkraut Hot Pot】酸菜火锅吃到饱

让UITableView来表演 Day6

前几天都跑去快乐学潜水,每天回家还要生出内容,不免的前几篇有些偷懒,当然之後会把前面的一些内容多给补...

10.MYSQL运算子

每一种语言都有自己运算的符号,SQL也不例外,下面帮大家整理了一些常用的一些运算子,这些运算子会在条...

[JS] You Don't Know JavaScript [Async & Performance] - Promises

前言 在上一张中我们介绍了使用callback function的目的与缺点,虽然可以帮助我们处理非...

【JavaScript】阵列方法之map()

【前言】 本系列为个人前端学习之路的学习笔记,在过往的学习过程中累积了很多笔记,如今想藉着IT邦帮忙...

Day 16 分享一下研究 Compose UI 到目前的心得

今年的疫情蛮严重的,希望大家都过得安好,希望疫情快点过去, 能回到一些线下技术聚会的时光~ 祝福大家...