Rails 5 Test Prescriptions(everday Rspectest做者推薦) 目錄 1-3章

總文檔鏈接: git

RSpec.info/documentation/ 編程

 

 

如何使用TDD 和 自動化測試來創建一個Rails app。緩存

TDD讓你用測試來探索代碼的設計。你將學習可利用的工具,並學習用什麼工具最好使。Tools comes and tools go, 工具是不斷進化的,因此做者但願讀者用最少的步驟寫出更好的代碼。安全

 

to help you write great app that do cool things and still catch the train home!ruby

 

What's in this book?app

開始介紹TDD,它爲何起做用,什麼時候用TDD。less

而後2章將使用RSpec來爲新的Rails app 建立test.ide

以後幾章節將單元測試基礎,關於models, 多種方法生成測試數據,使用test doubles(替身) to simulate objects and specify hard-to-reach states.工具

而後end-to-end tests 集成測試和Capybara.單元測試

討論JavaScript 先學習有JS 代碼的end-to-end tests,而後學習JS單元測試。

而後,旅行到其餘Rails部分,展現系統支持的工具。

12章還會了解使用Minitest來代替RSpec.

13-14章關於指定的場景測試,包括安全測試,測試第三方services.

15章 Debugging and Troubleshooting failing test.⚠️陌生

16章 關鍵寫快速的代碼和快速的寫代碼

17章 Legacy code 遺產代碼,從他人那裏繼承的代碼,等同於bad code. 

 

 What You'll Need

最新的Ruby2.5和Rails5.2,RSpec 3.7.1, Minitest 5.11.3


做者說RSpec學習曲線稍微陡峭,但這是業界使用最多的工具。Minitest 學習起來比較容易。

也就是常常說的: sometimes the best practice for learning isn't the best practice for experts. 

 

本書版本的更新:

 

  • 控制器測試被拋棄,集成測試會被結合RSpe講解
  • JS內容是新增的,包括集成和單元測試。Rails Webpacker來開發JS代碼
  • Capybara 集成如今使用headless Chrome做爲Javascript driver。
  • 代碼樣本從寫。 factory_bot做爲數據建立方法使用在最新的測試中。 

 

下載代碼案例 

 


第一章 一個預言故事

不寫測試的問題是,重構的時候還要手動測試,而寫自動化測試幾行不花費額外的時間。 

寫自動化測試能夠防止你忘記測試的步驟。 

if you do testing well, your work will go faster。

TDD能夠減小bug而且容易改正bug。 

TDD步驟:

 

  1. 建立測試。每一個測試都應當簡單,作一件事情。
  2. 確保測試失敗。
  3. 寫最簡單的代碼來經過當前測試。不要擔憂完整的代碼,不要過於向前,步子要小。 
  4. 測試經過後,重構改進代碼。 去除重複。而後再運行測試。
  5. 重複以上步子
TDD的部分缺點:

當你不知道程序須要作什麼的時候,TDD沒有什麼用。由於若是你不指定用什麼斷言/指望,就無法寫測試。 好比view-testing:這時須要先寫一部分代碼而後立刻測試,靈活一點,做者稱爲:test-next mode。

 

 

在寫測試前,先列出一個測試清單,註明你要測試的內容,以防忘記。

在rails 社區,仍有討論TDD 破壞代碼的問題。 

做者認爲TDD開發不能取代好的設計天賦,TDD仍可能創造bad code。 

 


第2章Test-driven development Basics

Prescription3:(藥方->決策)

Initializing objects is a good starting place for a TDD process. Another good approach is to use the test to design what you want a successful interaction of the feature to look like. 

 

下面跟着案例章節走。只記錄重點。 

Install RSpec

git init, #創建版本控制系統。

mkdir gatherer  -> cd gatherer

rails new .    -> bundle install

rake db:create:all   -> rake db:migrate

而後,安裝gem 'rspec-rails'  ->再次bundle install

rails generate rspec:install #生成初始化文件

⚠️在.rspec, 加上--format documentation

rails spec  #測試是否安裝成功。

 

Where to Start?

初始化對象是 TDD驅動測試開發的好的開始。另外一個好方法是使用test來設計一個看起來成功的交互的功能。

創建spec/models/project_spec.rb,而後創建一個project的初始狀態:

require 'rails_helper'
Rspec.describe Project do
  it "considers a project with no tasks to be done" do
    project = Project.new
    expect(project.done?).to be_truthy
  end
end

註解

done?是自定義的方法, be_truthy是內建匹配器,判斷actual_value是不是真,不是nil或false。

這是結構:expect(actual_value).to matcher

expect是RSpec定義的方法,接收任意object做爲參數並return一個特殊的RSpec對象,這個對象被稱爲 ExpectationTarget. 

而後這ExpectationTarget將做爲matcher方法的參數,最後返回結果。

  

Running the test

使用rspec, 全部目錄的文件都會被加載。

每一個RSpec文件須要rails_helper文件加載Rails環境,和spec_helper.rb,這裏包含非Rails的步驟。

rails_helper.rb會創建固件或預置件。

每一個頂級call to RSpec.describe建立一個內部RSpec對象叫作 example group.

example group使用塊參數讓describe方法被執行。describe方法裏面也能夠內嵌example groups。

每一個descirbe方法內可能包含it方法,每一個it方法創造一個獨立測試,這個測試叫作example。

在每一個example group中先運行before(:all) 在全部案例運行結束後,運行after(:all)

 

每一個案例也有before(:example), after(:example) 鉤子方法。

 

Making the Test Pass

 

運行rspec會報錯, uninitialized constant Project,固然了我沒創建這個類。

 

而後有3個不一樣的解決辦法:

 

  • 最purist way: 寫最少的代碼讓當前錯誤經過,不考慮更大範圍的事情。
  • Practical way: 寫你須要最終寫的代碼,忽略過小的沒有價值的步驟(不用測試了)
  • The teaching way: 介於前二者之間。讓TDD既不要陷於細節之中,也不會忽略過多步驟。

 

做者的態度,偏重purer。做者的經歷,有時候由於過於實際了,致使問題沒有理解和忽略了本該測試的步驟。

 

app/models/project.rb

class Project

end 

而後再$rspec , ->undefined method `done?' for <Project:0x00007ff65db9b9b8> 

定義done?方法,再測試->expected: truthy value      got: nil

class Project
  def done?
    true
  end
end

測試經過! 

 


The Second Test(只記錄重點。)

創建了Task.Project and Task都沒有繼承ActiveRecord。做者的目的是一步步來。


 

let 

 

使用let重構。 let(:project) {Project.new}

let方法是一個語法糖。定義一個方法,調用這個方法會緩存這個結果。相似:

def me
  @me ||= User.new( name:   "Noel" )
end 

不調用就不存在。

let!  則是在定義let方法後,就始終存在:project變量 

 


 

be_comlete匹配器,是自定義的。

 

class Task
  def initialize
    @completed = false
  end
  def mark_completed
    @complete = true
  end
  def complete? #必定帶 問號,才能成爲匹配器be_complete
    @completed
  end
end

 

RSpec.describe Task, type: :model do
  let(:task) {Task.new}
  it "does not have a new task as complete" do
    expect(task).not_to be_complete
  end

 若是沒有定義方法 complete?,測試會報錯:

expected #<Task:0x00007fe3ac637da0 @completed=false> to respond to `complete?` 

 


 知識點: 

  def done?
    tasks.all? {|task| task.complete? }
    # task.all?(&:complete?)
    #這是單塊方法,塊參數的變形,具體需看<Ruby元編程>。 
  end
    # Enumerable#all?  傳遞每一個元素到塊若是每次塊返回的是true,則all?方法返回true,不然返回false

 

 


 

Adding Some Math 

 做者在寫測試前會想這個測試須要什麼,典型的測試結構🈶️3部分:

 

  1. Given: What data does the test need?
  2. When:  What action is taking place?
  3. Then :  what result(behavior) do I need to specify? 
 

Prescription:

選擇你的測試數據,定義易於理解不重複的測試變量名字, 一旦發送錯誤,就容易診斷。

 

 

 


 

 知識點:

class Task
  attr_accessor :size, :completed
  def initialize( options = {})
    @completed = options[:completed]
    @size = options[:size]
  end

end

 

task1 = Task.new(size:1, completed: true) 

存儲的 task1.@size 是1,

初始化參數是options= {size:1, completed: true}

因此@size = options[:size] = 1 


 

 The First Date

目的:把未完成的task和完成的task做個區分的同時,根據完成時間,體現出是近期完成(21天內),仍是非近期完成。 

根據測試數據的思考設計3步驟:

 

  1. Given , 這個測試須要什麼數據(測試的目的是什麼)?完成的時間
  2. When,  什麼行爲/方法正在發生?  task獲得completed_at完成時間數據
  3. then,   須要指定什麼樣子的結果behavior?  若是是近期的完成的工做,則返回ture。

 

 

增長2個案例: 

    it "doesn't count an incomplete task toward velocity" do
      expect(task).to_not be_a _part_of_velocity
    end

 

    it "counts a recently completed task toward velocity" do
      task .mark_completed(1.day.ago)
      expect(task).to be_part_of_velocity
    end 


修改代碼:

⚠️a boundary-condition test :數據的設置體如今邊界線及其兩邊。

 把Task表的completed屬性類型從boolean改成date,名字改成completed_at

 

  def initialize(options = {})
    @size = options[:size]
    if options[:completed_at]
      mark_completed(options[:completed_at])
    end
  end
  def mark_completed( date= Time.current) #默認爲當前時間
    @completed_at = date
  end

修改task.rb,增長1個方法。part_of_velocity

  def part_of_velocity?
    unless complete?
      return false
    end
    completed_at > 21.days.ago
  end

知識: Float#nan?

若是是一個不可驗證的浮點數則返回NaN, 這個nan?方法是驗證是不是nan 

a = 0.0/0.0   #=> NaN
a.nan?        #=> true

 


 

知識: 

對應可能會發生變更的數字,這個數字用到了多個不一樣類的實例方法中,可使用類方法,而不是常量儲存preserve這個數字。

  def self.velocity_length_in_days
    21
  end

之後改動就方便了。

 


 

What You've Done

Write a simple test, write simple code to make it pass, and refactor. 

TDD 的做用

當你開始作一個需求,而把這個需求轉變爲邏輯程序,你不能當即就弄清楚這個轉變。

使用TDD你能夠逐步的攻克這個問題。從小處,利於理解的角落開始,增長了對需求問題的理解後再開始較難的部分。 

相關文章
相關標籤/搜索