繼續把model
的測試寫完
private方法
屬於不直接測試,利用有用到它們的public
方法測試即可。類別方法與實體方法都應該要測試。
我新增了一些測試,假設都是我想增加的功能。
require 'rails_helper'
RSpec.describe Role, type: :model do
let!(:role) { create(:role) }
describe 'associations' do
end
describe "測試驗證功能" do
end
describe "#attack_power" do
it "基本攻擊力等於力量*10"
it "力量是6基本攻擊力為60"
it "力量是4基本攻擊力為40"
it "武器DPS是所持sword的dps"
it "min = 2 , max = 4 , dps = 3"
it "真正攻擊力等基本攻擊力*武器DPS"
it "力量4 武器min:2, max:5 真正攻擊力120"
end
end
為了有紀錄力量
、基本攻擊力
、真正攻擊力
與sword_dps
,在Role
加上這些些欄位。Sword
我們一樣假設測試過了。不良示範了
Role
$ rails g migration add_role_column_about_attack_power
class AddRoleColumnAboutAttackPower < ActiveRecord::Migration[6.1]
def change
add_column :roles, :power, :integer
add_column :roles, :attack_power, :integer
add_column :roles, :really_attack_power, :integer
add_column :roles, :sword_dps, :integer
end
end
$ rails db:migrate
#帥一點的指令。
$ rails g migration AddColumnsToRole power:integer attack_power:integer really_attack_power:integer sword_dps:integer
$ rails db:migrate
Sword
$ rails g migration add_column_sword_min_max_damge
class AddColumnSwordMinMaxDamge < ActiveRecord::Migration[6.1]
def change
add_column :swords, :min_damge, :integer
add_column :swords, :max_damge, :integer
end
end
$ rails db:migrate
Factory_bot
當然需要修改。
spec/factories/role
FactoryBot.define do
factory :role do
user
name { Faker::Name.first_name }
job { Faker::Job.title }
age { rand(5..130)}
power {5}
attack_power {nil}
really_attack_power {nil}
after :create do |role|
create_list :sword, 3, role: role #has_many這樣建立,數字代表建立幾個。
#create :sword, role: role ##has_one這樣建立。
end
end
end
spec/factroies/sword
FactoryBot.define do
factory :sword do
role
min_damge { 3 }
max_damge { 6 }
end
end
都是簡單計算,省略過程。
role.spec.rb
describe "#attack_power" do
it "基本攻擊力等於力量*10" do
expect(role.attack_power).to be(role.power * 10)
end
it "力量是6基本攻擊力為60" do
role.power = 6
expect(role.attack_power).to be(60)
end
it "力量是4基本攻擊力為40" do
role.power = 4
expect(role.attack_power).to be(40)
end
it "武器DPS是所持sword的dps" do
expect(role.sword_dps).to be(role.swords.first.dps)
end
it "min = 2 , max = 4 , dps = 3" do
role.swords.first.min_damge = 2
role.swords.first.max_damge = 4
expect(role.sword_dps).to be(3)
end
it "真正攻擊力等基本攻擊力*武器DPS" do
expect(role.really_attack_power).to be(role.attack_power * role.sword_dps)
end
it "力量4 武器min:2, max:5 真正攻擊力120" do
role.power = 4
role.swords.first.min_damge = 2
role.swords.first.max_damge = 5
expect(role.really_attack_power).to be 120
end
end
role.rb
class Role < ApplicationRecord
def attack_power
power * 10
end
def really_attack_power
attack_power * sword_dps
end
def sword_dps
@sword = self.swords.first
@sword.dps
end
end
sword.rb
class Sword < ApplicationRecord
#略...
def dps
(min_damge + min_damge) / 2
end
end
到這邊rspec
也沒有問題。
role.rb
的sword_dps
變成private
。role.rb
private
def sword_dps
@sword = swords.first
@sword.dps
end
end
rspec
畫面會出現錯誤,都是跟private
有關的。
內容都是找不到sword_dps
這方法了。
.............FFF.
Failures:
NoMethodError:
private method sword_dps called for #<Role:0x00007f814d0161b8>
#略...
rspec ./spec/models/role_spec.rb:62 # Role#attack_power 武器DPS是所持sword的dps
rspec ./spec/models/role_spec.rb:66 # Role#attack_power min = 2 , max = 4 , dps = 3
rspec ./spec/models/role_spec.rb:72 # Role#attack_power 真正攻擊力等基本攻擊力*武器DPS
噗噗,就算再簡單,花時間的測試,等於沒測了。
很多人會這樣說,私有方法不測試,應該更專注在你的公開方法上,因為要private
就代表不是要直接給使用者使用,或許因為商業邏輯很複雜,或許其他Model
也會一直用到這個方法,願意的話測試也可以,但最終只會刪除或變成無意義的註解,測試會變得沒有意義,心裡面安心而已。
真的要測可以利用send
方法處理(callback部分有示範),或是像我這樣,測了再刪。
可以看到我最後一個測試就是有調動到私有方法的,所以我可以安心的把這三個測試刪除了(我在自己本機上是註解)。
這個部分並不是在分享該怎麼做
我也是Rspec
新手,但若確定自己要建立私有方法,照大家說的私有方法不測試,那代表我一開始測試的內容就不該那樣設定。重點是怎麼規畫好測試流程。
Callback的測試與私有方法很類似,就是測試有調動到Callback
的方法就好。
而部分會用到Callback
來執行的方法,可能都會成為private....
所以我今天的code
到最後可能只會剩下下面那樣。
Role
before_update :attack_power, :really_attack_power
rspec/model/role_spec.rb
describe "#call_back before_update" do
it "素質更改後,攻擊力會更改" do
create(
:role,
name: "測試用",
job: "戰士",
age: "29",
power: "6",
attack_power: nil,
really_attack_power: nil
)
role.power = 5
role.save
expect(role.send(:attack_power)).to be(50)
expect(role.send(:really_attack_power)).to be(200)
end
end
在我本機上,我是沒將這兩個方法設私有,雖然只是簡單的code
要直接刪掉還真捨不得?
所以在設計時,就真的該想好哪些方法是private
方法。
今日的檔案:https://github.com/nauosika/Rspec_test/tree/D14
Model
雖然是最簡單的測試,但幫我自己紀錄一下怎麼開頭Rspec
。
關於分享測試就到今天結束了。
今天的leetcode.628:Maximum Product of Three Numbers
題目連結:https://leetcode.com/problems/maximum-product-of-three-numbers/
題目重點:考慮到負負得正,負負負會得負,正正負會得負,還有負數-1
最大-1000
最小。
#錯的
def maximum_product(nums)
ans = 1
new_arr = nums.max(3)
new_arr.each do |num|
ans *= num
end
ans
end
這是我一開始的解法,沒有考慮到值有超過三個時,兩個很大的負數相乘再乘一個最大的正數,才會是正解。submit
出去後發現還有一個例子。
[-100, -98, -1, 2, 3, 4]
但這個例子出現,其實也透露出答案了。
越小的負數,去掉負號後反而是最大的,以這個例子看就是前兩個與最後一個相乘最大。last * nums[0] * nums[1]
,如果都是正數那一定是max(3).reduce(:*)
,那就這兩個選大的。
不用去擔心陣列中數字很多,數字越多,這種狀況反而會越明顯。
def maximum_product(nums)
nums.sort!
[nums.max(3).reduce(:*), nums.last * nums[0] * nums[1]].max
end
這樣就解完了。