iT邦幫忙

1

Ruby基本介紹(八)Rspec幼稚園等級實作

  • 分享至 

  • xImage
  •  

DDU-DU DDU-DU~

Yes

十幾億的觀看次數...跟四十多億次的某隻MV還有一段距離XD


預防針:由於是菜鳥分享心得,以下測試內容,非常基礎。

如果是初學神書內容部分覺得太難,再來看我的超級幼稚園等級的Rspec流程。

請確定已安裝gem Rspec

$ gem install rspec

幼稚園等級規格書:產生一把新的劍,劍有最大最小攻擊力。

請先建立model,file/sword.rb

class Sword
end

接著建立測試檔案file/sword_spec.rb

require "./sword"  

RSpec.describe Sword do
  context "規格書大項" do
    it "拆分細節處理"
  end
end

把內容寫入

require "./sword"

RSpec.describe Sword do
  context "產生一把新劍" do
    it "會有最小攻擊力"
    it "會有最大攻擊力"
  end
end

執行測試指令,應該會跑出下面畫面

$ rspec sword_spec.rb         
**

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) Sword 產生一把新劍 會有最小攻擊力
     # Not yet implemented
     # ./sword_spec.rb:5

  2) Sword 產生一把新劍 會有最大攻擊力
     # Not yet implemented
     # ./sword_spec.rb:6


Finished in 0.00251 seconds (files took 0.07607 seconds to load)
2 examples, 0 failures, 2 pending

如果沒問題,我們繼續對測試檔案做修改

require "./sword"

RSpec.describe Sword do
  context "產生一把新劍" do
    it "會有最小攻擊力" do
      @sword = Sword.new
      @sword.min_attack
    end
    it "會有最大攻擊力" do
      @sword = Sword.new
      @sword.min_attack
    end
  end
end

執行測試後觀察,錯誤內容應該都是沒有方法。

  NoMethodError:
       undefined method `min_attack' for #<Sword:0x00007fe749a4b928>
       
  NoMethodError:
       undefined method `min_attack' for #<Sword:0x00007fe749a437f0>

那我們便製造方法。

#file/sword.rb
class Sword

  def initialize
    @sword = {min_attack: nil, max_attack: nil}
  end
  
  def min_attack
  end

  def max_attack
  end
  
end

跑測試會發現兩個測試項目都綠燈了。
那我們增加一點點點規格書難度。

幼稚園等級規格書:產生一把新的劍,劍有最小攻擊力,在1~3之間。會有最大攻擊力在4~6之間。

require "./sword"

RSpec.describe Sword do
  context "產生一把新劍" do
    it "最小攻擊力在1~3之間" do
      @sword = Sword.new
      @min_attack = @sword.min_attack
      expect(@min_attack).to match 1..3
    end
    it "最大攻擊力在4~6之間" do
      @sword = Sword.new
      @max_attack = @sword.max_attack
      expect(@max_attack).to match 4..6
    end
  end
end

#to be, to match都是Rspec內有的語法,請自行查一下Rspec的說明書。

執行測試後,可以發現錯誤

  1) Sword 產生一把新劍 最小攻擊力在1~3之間
     Failure/Error: expect(@sword.min_attack).to match 1..3
       expected nil to match 1..3
     # ./sword_spec.rb:8:in `block (3 levels) in <top (required)>'

  2) Sword 產生一把新劍 最大攻擊力在4~6之間
     Failure/Error: expect(@sword.max_attack).to match 4..6
       expected nil to match 4..6
     # ./sword_spec.rb:13:in `block (3 levels) in <top (required)>'

因為我們沒有讓這兩個方法(min,max_attack)有回傳值,所以都出現nil to match,那我們就增加流程控制並回傳正確值。

class Sword

  def initialize
    @sword = {min_attack: nil, max_attack: nil}
  end

  def min_attack
    @sword[:min_attack] = rand(1..3)
  end

  def max_attack
    @sword[:max_attack] = rand(4..6)
  end

end

雖然有語法上的漏洞,但是我們發現測試過了,先不理會漏洞,我們再加工一下測試檔案

require "./sword"
RSpec.describe Sword do
  context "產生一把新劍" do
    it "最小攻擊力在1~3之間" do
      @sword = Sword.new
      @min_attack = @sword.min_attack
      expect(@min_attack).to match 1..3
      p @sword
    end
    it "最大攻擊力在4~6之間" do
      @sword = Sword.new
      @max_attack = @sword.max_attack
      expect(@max_attack).to match 4..6
      p @sword
    end
  end
end

#執行測試,並印出我們的實體。

可以發現@sword

#<Sword:0x00007fbb47b932a0 @sword={:min_attack=>2, :max_attack=>nil}>
#<Sword:0x00007fbb47b805b0 @sword={:min_attack=>nil, :max_attack=>5}>

不是一被new出來時,就有正確的數值在裡面了,所以我們再改一下Sword.rb。

class Sword
  def initialize
    @sword = {min_attack: nil, max_attack: nil}
    self.min_attack
    self.max_attack
  end

  def min_attack
    @sword[:min_attack] = rand(1..3)
  end

  def max_attack
    @sword[:max_attack] = rand(4..6)
  end
end

再跑一次測試,可以發現new新的Sword時,實體已經確認建立完整。

當然實務上,可能沒有人會去測試開發資料表,也可能都是建立DB後,去針對model及controller所需要的新功能去做TDD。

這整個測試很簡陋。
那我們來直接為已經建立好的這個model再加一個小功能。

規格書:Sword需要一個新的功能,可以附魔,附魔之後最大攻擊力隨機+2~4,,但最大攻擊力不可以超過10。
建立一個新的測試檔案方便觀察(可用前一個測試檔)
直接完成後如下

require "./sword"
RSpec.describe Sword do
  context "附魔" do
    it "新劍可以操作附魔,最大攻擊力+2~4,但最大攻擊力不可以超過8" do
      @sword = Sword.new
      p @sword
      @sword.enchant
      expect(@sword.enchanted).to be <= 8
      p @sword.enchanted
      p @sword
    end
  end
end

sword.rb會多出

def enchant
  @sword[:max_attack] += rand(2..4)
  if @sword[:max_attack] > 8
    @sword[:max_attack] = 8
  end
end

def enchanted
  @sword[:max_attack]
end

可以發現物件在正個操作流程中,都會一直受到改變。

在rails上測試與在ruby上會有不同的地方,主要是因為框架建立了更多ruby本身沒有的功能,所以在測試上感受會有所不同。

最後記得TDD是一種行為,要把這個行為變成一個技能...要很努力很努力。

超幼稚園等級TDD分享完成....又再騙字數了


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言