iT邦幫忙

2021 iThome 鐵人賽

DAY 14
0
自我挑戰組

初級紅寶石魔法師心得分享。系列 第 14

D-16. Rspec 從零開始寫測試(二) factory_bot_rails && Largest Number At Least Twice of Others

  • 分享至 

  • xImage
  •  

今日繼續,由此分支內容開始。
https://github.com/nauosika/Rspec_test/tree/D_17_Rspec_content

安裝factory_bot_rails

增加了factory_bot_rails於Gemfile,faker有玩LOL的都認識,不介紹了。

group :development, :test do
  #略...
  gem 'factory_bot_rails', '~> 6.2'
  gem 'faker', '~> 2.19'
end

官方使用手冊
factory_bot_railshttps://github.com/thoughtbot/factory_bot_rails
faker https://github.com/faker-ruby/faker

factory_bot_railsfactory_bot? 前者包含後者,針對Railsintegrations。

bundle後我們需要對factory_bot_rails做一些設定,才能讓其協助Rspec
一樣照著手冊新增spec/support/factory_bot.rb。將其加入以下代碼。

require 'factory_bot'  #千萬不要忘了這個
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end

接著在spec/rails_helper.rb下加入以下代碼。

require 'support/factory_bot'

到這裡算是完成第一步。


建立工廠

接著我們新增一個spec/factories資料夾。
依照我昨天role建立檔案。spec/factories/roles.rb。可以只建立factories.rb檔案,將資料都寫入其內,我沒試過,覺得有分類還是比較好。

FactoryBot.define do
  factory :role do
    name { "王大明" }
    job  { "宅男" }
    age { 18 }
  end
end

接著改寫spec/models/role_spec.rb

require 'rails_helper'

RSpec.describe Role, type: :model do

  describe "測試驗證功能" do
    context "所有欄位不能空白" do

      let(:role) { create(:role) }

      it "name不可以空白" do
        role.name != nil
        expect(role).to_not be_valid
      end

      it "job不可以空白" do
        role.job != nil
        expect(role).to_not be_valid
      end

      it "age不可以空白" do
        role.age != nil
        expect(role).to_not be_valid
      end
    end
  end
end

我故意將=!=是為了確認設定工廠是否建立成功。
let(:role) { create(:role) }主要是改寫這樣,會預設由工廠給的資料處理。
let(:role) { create(:role, name: "小當家", job: "廚師", age: "20") }也還是可以複寫參數。

rspec畫面應該如下

FFF

Failures:

  1) Role 測試驗證功能 所有欄位不能空白 name不可以空白
     Failure/Error: expect(role).to_not be_valid
       expected #<Role id: 82, name: "王大明", job: "宅男", age: 18, created_at: "2021-09-10 05:36:29.336135000 +0000", updated_at: "2021-09-10 05:36:29.336135000 +0000"> not to be valid
     # ./spec/models/role_spec.rb:12:in `block (4 levels) in <top (required)>

  2) Role 測試驗證功能 所有欄位不能空白 job不可以空白
     Failure/Error: expect(role).to_not be_valid
       expected #<Role id: 83, name: "王大明", job: "宅男", age: 18, created_at: "2021-09-10 05:36:29.353380000 +0000", updated_at: "2021-09-10 05:36:29.353380000 +0000"> not to be valid
     # ./spec/models/role_spec.rb:17:in `block (4 levels) in <top (required)>

  3) Role 測試驗證功能 所有欄位不能空白 age不可以空白
     Failure/Error: expect(role).to_not be_valid
       expected #<Role id: 84, name: "王大明", job: "宅男", age: 18, created_at: "2021-09-10 05:36:29.355158000 +0000", updated_at: "2021-09-10 05:36:29.355158000 +0000"> not to be valid
     # ./spec/models/role_spec.rb:22:in `block (4 levels) in <top (required)>

Finished in 0.03118 seconds (files took 0.83289 seconds to load)
3 examples, 3 failures

Failed examples:
#略...

確認工廠資料都有帶入後,我們來使用faker

faker產生假資料。

spec/factories/roles.rb改寫如下。

Faker::Config.locale = 'zh-TW'
FactoryBot.define do
  factory :role do
    name { Faker::Name.first_name }
    job { Faker::Job.title }
    age { rand(5..130)}
  end
end

正規測試應該不會去用Faker::Config.locale = 'zh-TW',這邊娛樂性質。稍後我就會將此行刪除。faker支援產生中文資料沒錯,但可以產生資料有限,請見官方Githublib/locales/zh-TW.yml,不支援中文部分還是產生英文。

跑一下rspec應該可以看到以下類似畫面。

1) Role 測試驗證功能 所有欄位不能空白 name不可以空白
     Failure/Error: expect(role).to_not be_valid
       expected #<Role id: 94, name: "嘉慧", job: "Advertising Analyst", age: 116, created_at: "2021-09-10 05:53:07.818665000 +0000", updated_at: "2021-09-10 05:53:07.818665000 +0000"> not to be valid
     # ./spec/models/role_spec.rb:12:in `block (4 levels) in <top (required)>

  2) Role 測試驗證功能 所有欄位不能空白 job不可以空白
     Failure/Error: expect(role).to_not be_valid
       expected #<Role id: 95, name: "志銘", job: "IT Supervisor", age: 27, created_at: "2021-09-10 05:53:07.836171000 +0000", updated_at: "2021-09-10 05:53:07.836171000 +0000"> not to be valid
     # ./spec/models/role_spec.rb:17:in `block (4 levels) in <top (required)>

  3) Role 測試驗證功能 所有欄位不能空白 age不可以空白
     Failure/Error: expect(role).to_not be_valid
       expected #<Role id: 96, name: "惠玲", job: "Chief Consulting Consultant", age: 115, created_at: "2021-09-10 05:53:07.838495000 +0000", updated_at: "2021-09-10 05:53:07.838495000 +0000"> not to be valid
     # ./spec/models/role_spec.rb:22:in `block (4 levels) in <top (required)>

Finished in 0.18368 seconds (files took 0.84862 seconds to load)
3 examples, 3 failures

可以看到faker生效了。
那我們再新增一些需測試的功能及將!=改回=
我會順便把Faker::Config.locale = 'zh-TW'刪除。

spec/models/role_spec.rb目前更改為。

require 'rails_helper'

RSpec.describe Role, type: :model do

  describe "測試驗證功能" do
    context "欄位限制" do
      let(:role) { create(:role) }

      it "name不可以空白" do
        role.name = nil
        expect(role).to_not be_valid
      end

      it "job不可以空白" do
        role.job = nil
        expect(role).to_not be_valid
      end

      it "age不可以空白" do
        role.age = nil
        expect(role).to_not be_valid
      end

      it "name不能取太長" do
        role.name = 'a' * 11
        expect(role).to_not be_valid
      end

      it "job不能太短" do
        role.job = 'a'
        expect(role).to_not be_valid
      end

      it "age只能數字" do
        role.age = "數字"
        expect(role).to_not be_valid
      end
    end
  end
end

app/models/role.rb

class Role < ApplicationRecord
  validates :name , presence: true, length: { maximum: 10 }
  validates :job , presence: true, length: { minimum: 2 }
  validates :age , presence: true, numericality: { only_integer: true }
end

rspec畫面。

......

Finished in 0.18241 seconds (files took 0.79726 seconds to load)
6 examples, 0 failures

使用factory_bot_rails可以讓資料更多樣化,提高測試測試正確度。

今天的分支:https://github.com/nauosika/Rspec_test/tree/D_16_Rspec_content

明天透過gem來簡化測關聯性。


今天的leetcode747. Largest Number At Least Twice of Others
題目連結:https://leetcode.com/problems/largest-number-at-least-twice-of-others/
題目重點:熟悉陣列很easy。看清楚要回傳什麼就好。

# @param {Integer[]} nums
# @return {Integer}
def dominant_index(nums)

end

puts dominant_index([3,6,1,0]) #=> 1
puts dominant_index([1,2,3,4]) #=> -1
puts dominant_index([1]) #=> 0

語法。

2.7.3 :017 > [3,6,1,0].max(2)
 => [6, 3]

那就是判斷這兩個,大的大於小的兩倍就是回傳6在原陣列的位置。

2.7.3 :018 > [6, 3].max >= 2 * [6, 3].min
 => true

一般看到例子三就直接先想到

return 0 if nums.size == 1

解法:

def dominant_index(nums)
  return 0 if nums.size == 1
  max_array = nums.max(2)
  max_array.max >= (max_array.min * 2) ? nums.index(max_array.max) : -1
end

可是觀察一下可以發現

2.7.3 :017 > [3,6,1,0].max(2)
 => [6, 3]

那其實可以

2.7.3 :019 > a, b = [3,6,1,0].max(2)
 => [6, 3]
2.7.3 :020 > a
 => 6
2.7.3 :021 > b
 => 3
#a永遠是最大那個。

還有

2.7.3 :022 > a, b = [1].max(2)
 => [1]
2.7.3 :023 > a
 => 1
2.7.3 :024 > b
 => nil

所以判斷式如果滿足

 if b false || a > 2*b

那反而不用第一行,因為

2.7.3 :027 > a, b = [1].max(2)
 => [1]
2.7.3 :028 > [1].index(a)
 => 0

簡化:

def dominant_index(nums)
  largerest, larger = nums.max(2)
  !larger || largerest >= 2*larger ? nums.index(largerest) : -1
end

上一篇
D-17. Rspec 從零開始寫測試(一)
下一篇
D-15.Rspec 從零開始寫測試(三) shoulda-matchers && Distribute Candies
系列文
初級紅寶石魔法師心得分享。30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言