2017年5月10日 星期三

第三次上五倍紅寶石rails課 5/10 - TDD

今天上的是TDD,範例其實很簡單,但就算這樣想當初第一次上的時候我因為完全沒有這個概念,所以滿頭問號,畢竟那個時候我連更基礎的東西其實都還沒搞懂。

所以我其實很羨慕現在幾乎旁邊都有排一個實習生坐在旁邊的設計。
(不過我當初那個樣子我其實也不知道該從哪邊問起好,大概就像一個一般國中數學也2266的人你突然講微積分給他聽,就算他聽不懂也不知道該從哪問好差不多(?))

不過TDD讓我覺得很有親近感,大概因為我是QA出身?,雖然這是兩個不同的概念(p.s QA要做什麼依公司不同可能沒有有些人想的那麼單純,不過這邊省略介紹XD)

http://railsbook.tw/chapters/24-testing-with-rspec-part-2.html
存錢功能 原本帳戶有 10 元,存入 5 元之後,帳戶餘額變 15 元 原本帳戶有 10 元,存入 -5 元之後,帳戶餘額還是 10
元(不能存入小於等於零的金額) 領錢功能 原本帳戶有 10 元,領出 5 元之後,帳戶餘額變 5 元 原本帳戶有 10 元,試圖領出 20
元,帳戶餘額還是 10 元,但無法領出(餘額不足) 原本帳戶有 10 元,領出 -5 元之後,帳戶餘額還是 10
元(不能領出小於或等於零的金額)
RSpec.describe BankAccount do 

end
執行會出現下面錯誤,因為還沒有BankAccount這個class
NameError:
  uninitialized constant BankAccount

硬讓他過的方法

既然他是說找不到BankAccount這個常數,那我給他一個不就好了==+
反正他就只是一個會指到BankAccount這個class的常數
但是這樣寫code review的時候大概會被打…
BankAccount = 1

RSpec.describe BankAccount do 

end

番外篇

讓class用小寫的方法,雖然實務上用不到..
這邊cc就只是一個指向class的變數
同理上面的RSpec.describe BankAccount的BankAccount也是一樣的概念,就一個指到BankAccount class的常數
cc = Class.new do 
    def hello
        puts "hello"
    end
end

c = cc.new
c.hello

開始寫吧

上面class也用好之後執行rspec會看到很多像下面那種訊息,說找不到method
NoMethodError:
       undefined method `deposit' for #<BankAccount:0x007f9b0b6121c0 @amount=10>
要解決很簡單~給他一個就好了~就算裡面是空的也可以
def deposit(amount)
end
把該加的method都給他之後,現在錯誤訊息應該會像下面這樣
       expected #<Fixnum:31> => 15
            got #<Fixnum:21> => 10
原因也很簡單,因為現在method都是空的,裡面沒有做任何的運算,當然數字不會變成15,正確應該要改成這樣
class BankAccount

  def initialize(amount)
    @amount = amount
  end

  def deposit(amount)
    @amount += amount
  end

  def balance
    @amount
  end
end
這樣子”原本帳戶有 10 元,存入 5 元之後,帳戶餘額變 15 元“就會pass了
可是存款不可能存負得值進去,所以要加一下判斷
 def deposit(amount)
    @amount += amount if amount > 0
  end
這樣子“存錢功能”兩個就都會pass了。
領錢和提錢的概念差不多,下面是完整的樣子
class BankAccount
  def initialize(amount)
    @amount = amount
  end

  def deposit(amount)
    @amount += amount if amount > 0
  end

  def balance
    @amount
  end

  def withdraw(amount)
    # ">0"可以用positive?代替,可是這樣多打好多字
    if amount > 0 && enough?(amount)
      @amount -= amount
    end
  end

  private
  def enough?(amount)
    balance >= amount
  end
end


RSpec.describe BankAccount do 

  describe "存錢功能" do 
    it "原本帳戶有 10 元,存入 5 元之後,帳戶餘額變 15 元" do 
      account = BankAccount.new(10)
      account.deposit 5
      expect(account.balance).to be 15
    end

    it "原本帳戶有 10 元,存入 -5 元之後,帳戶餘額還是 10 元(不能存入小於等於零的金額)" do 
      account = BankAccount.new(10)
      account.deposit -5
      expect(account.balance).to be 10
    end
  end

  describe "領錢功能" do 
    it "原本帳戶有 10 元,領出 5 元之後,帳戶餘額變 5 元" do 
      account = BankAccount.new(10)
      account.withdraw 5
      expect(account.balance).to be 5
    end
    it "原本帳戶有 10 元,試圖領出 20 元,帳戶餘額還是 10 元,但無法領出(餘額不足)" do
      account = BankAccount.new(10)
      account.withdraw 20
      expect(account.balance).to be 10
    end

    it "原本帳戶有 10 元,領出 -5 元之後,帳戶餘額還是 10 元(不能領出小於或等於零的金額)" do 
      account = BankAccount.new(10)
      account.withdraw -5
      expect(account.balance).to be 10
    end
  end

end

哎~可是好像很多一樣的code出現了好幾次,看起來好討厭

let

可以用let解決這個問題
let(:account) { BankAccount.new(10) }
雖然看起來像是變數,實際上他是幫你定義一個account方法所以如果你把測項裡的account改成account(),他還是會動,所以他真的是個method,只是在ruby裡面小括號是可以被省略的。
    it "原本帳戶有 10 元,存入 -5 元之後,帳戶餘額還是 10 元(不能存入小於等於零的金額)" do 
      account().deposit -5
      expect(account.balance).to be 10
    end

before

也可以用before來解決這個問題,但是因為必須要用實體變數下面的測項才有辦法用,所以要記得除了把本來每個測試裡面的account = BankAccount.new(10)刪掉以外,也要記得把account改成@account
  before :each do 
    @account = BankAccount.new(10)  #要用實體變數
  end

all

另外除了before也有all可以用,但all只會在最一開始被執行一次

完整版
class BankAccount
  def initialize(amount)
    @amount = amount
  end

  def deposit(amount)
    @amount += amount if amount > 0
  end

  def balance
    @amount
  end

  def withdraw(amount)
    # ">0"可以用positive?代替
    if amount > 0 && enough?(amount)
      @amount -= amount
    end
  end

  private
  def enough?(amount)
    balance >= amount
  end
end


RSpec.describe BankAccount do 

  let(:account) { BankAccount.new(10) }
  # def account
  #   @tmp ||= BankAccount.new(10)
  # end

  # #all只會跑一次
  # before :all do 
  #   puts "hi @ all"
  # end

  describe "存錢功能" do 
    it "原本帳戶有 10 元,存入 5 元之後,帳戶餘額變 15 元" do 
      account.deposit 5
      expect(account.balance).to be 15
    end

    it "原本帳戶有 10 元,存入 -5 元之後,帳戶餘額還是 10 元(不能存入小於等於零的金額)" do 
      account.deposit -5
      expect(account.balance).to be 10
    end
  end

  describe "領錢功能" do 
    it "原本帳戶有 10 元,領出 5 元之後,帳戶餘額變 5 元" do 
      account.withdraw 5
      expect(account.balance).to be 5
    end
    it "原本帳戶有 10 元,試圖領出 20 元,帳戶餘額還是 10 元,但無法領出(餘額不足)" do
      account.withdraw 20
      expect(account.balance).to be 10
    end

    it "原本帳戶有 10 元,領出 -5 元之後,帳戶餘額還是 10 元(不能領出小於或等於零的金額)" do 
      account.withdraw -5
      expect(account.balance).to be 10
    end
  end

end
Written with StackEdit.

沒有留言:

張貼留言