D-16. Rspec 从零开始写测试(二) factory_bot_rails && Largest Number At Least Twice of Others

今日继续,由此分支内容开始。
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

<<:  Day 14 Azure cognitive service: Text-to-Speech- Azure 念给你听

>>:  Day 14 实作 database migration

Day 29:Google Map 自订资讯视窗

本篇文章同步发表在 HKT 线上教室 部落格,线上影音教学课程已上架至 Udemy 和 Youtu...

【左京淳的JAVA WEB学习笔记】第八章 服务器异步设定

在特定时间开放抢票的网站,常常会有流量爆炸的问题。这时候可以透过服务器的异步处理来解决。 让买票处理...

1.3 Design System - 包含哪些项目要做?

职场好榜样F大 F 对我来说是设计的前辈,在他入职後,我才有在设计上有比较大幅度的进步 不过在他身...

13 出牌倒数要怎麽做才好?

整理一下算分数方法 这个方法实在太扯了,他很明显的有分成三个区块, 用 round 拿到该范围的卡 ...

[DAY26] 用 Azure Machine Learning SDK 来做 Pipeline

DAY26 用 Azure Machine Learning SDK 来做 Pipeline 在 A...