亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Ruby > 正文

用Ruby實現一個單元測試框架的教程

2020-10-29 19:41:52
字體:
來源:轉載
供稿:網友

在去年的YOW Melbourne開發者大會上,我參加了一些研習班。這些研習班由@coreyhaines和 @rains負責,因此TDD(測試驅動開發)成為了主要討論的內容。通常這不是一個問題,但是令人沮喪的是(考慮到這是2010年舉辦的開發者大會),那時上網還不是很方便,我剛裝上linux的筆記本無法下載Rspec。幸運的是幾周前,我決定自己寫一個單元測試框架(因為我有這個能力:)),接著我就有了一個可用的測試框架,問題解決了。但是,這讓我想到一個問題,最少可以用多少代碼寫成一個可用的單元測試框架?

一個最小可用的單元測試

剛開始寫一個單元測試框架的時候代碼是很少的,但當我想給它加入一些特性時就變得沒有那么精煉了:) 幸運的是重寫是很容易的。我們真正需要做的是執行下面的代碼:
 

describe "some test" do it "should be true" do  true.should == true end  it "should show that an expression can be true" do  (5 == 5).should == true end  it "should be failing deliberately" do  5.should == 6 endend

正如你看到的,它很像是一個基本的Rspec測試。讓我們寫一些代碼來執行它。

譯注:RSpec 工具是一個 Ruby 軟件包,可以用它構建有關您的軟件的規范。該規范實際上是一個描述系統行為的測試。

構建一個簡單的框架

首先要做的是使用“describe”來定義一個新的測試。既然我們想要把”describe” block放在任何地方(例如,文件本身),我們需要對Ruby做一點擴展。“puts”函數在Kernel block中,因此可以在任何地方使用(因為Object類包含了Kernel并且Ruby中的每個對象都繼承自Object類),同樣的我們會把describe放到Kernel block中以賦予同樣的能力):
 

module Kernel def describe(description, &block)  tests = Dsl.new.parse(description, block)  tests.execute endend

譯注:Ruby block:Ruby語言的block功能類似回調函數。

正如你看到的,”describe”接收一個用來描述測試的字符串和包含了測試代碼的block。在這里,我們將測試的代碼和”describe”分開講解(例如,”it” block)。因此我們創建了Dsl類,用它的parse函數處理待測試的block,結果會產生一個可以執行我們所有測試的對象,但是不要高興得太早。Dsl類看上去是這樣的:

class Dsl def initialize  @tests = {} end def parse(description, block)  self.instance_eval(&block)  Executor.new(description, @tests) end def it(description, &block)  @tests[description] = block endend

這里要做的是在Dsl對象的上下文里對block求值:
 

self.instance_eval(&block)

我們的Dsl對象有一個”it”函數,同樣也接收一個描述和一個block,這里和describe block包含的內容完全一致,一切都運行得很好(例如,我們基本上會在幾個函數調用時使用”it”函數,每次都傳入一個描述和一個block)。我們還可以在Dsl對象中定義其他的函數,并且這些函數會成為允許在”describe” block中使用的“語言”的一部分)。

在describe block中,”it”函數會為每個”it” block調用一次。每次調用時,會把輸入的block以測試描述作為鍵值存儲在哈希表中。完成這些以后,我們只要創建一個Executor對象,可以對我們所有的測試block進行迭代,調用它們并產生執行結果。Executor代碼如下:
 

class Executor def initialize(description, tests)  @description = description  @tests = tests  @success_count = 0  @failure_count = 0 end def execute  puts "#{@description}"  @tests.each_pair do |name, block|   print " - #{name}"   result = self.instance_eval(&block)   result ? @success_count += 1 : @failure_count += 1   puts result ? " SUCCESS" : " FAILURE"  end  summary end def summary  puts "/n#{@tests.keys.size} tests, #{@success_count} success, #{@failure_count} failure" endend

我們的executor代碼非常簡單。輸出”describe” block的描述,然后遍歷所有存儲的”it” block并且在executor對象中執行它們。這么處理沒有什么特別原因,但這意味著executor對象同樣也可以包含其他函數,并且可以在”it” block中作為一種“語言”來使用(比如,我們dsl的一部分可以定義為executor的一個函數)。譬如,我們可以在executor上定義下列函數:
 

def should_be_five(x) 5 == xend

這個函數同樣可以在”it” block內部使用,但對于我們這個簡單的測試沒有這個必要。

所以,”it” block會計算并存儲結果,通常結果只是”it” block最后一個語句的返回值(按照常規的Ruby)。這里,我們希望確保最后一個語句總是返回一個布爾值(標明測試通過或失敗),通過它我們可以輸出一些有意義提示。

我們還差最后一步,”should”函數代碼如下:
 

true.should == true5.should == 5

每個對象都應當提供自己”should”函數,代碼如下:
 

class Object def should  self endend

這個函數并沒有真正做什么工作(僅僅是返回對象本身);它僅僅是一個讓測試讀起來更好的語法。

在這個階段,我們只是將測試計算的結構轉換成一個字符串,表明測試結果通過或失敗并輸出。在這個過程中,我們會統計通過或失敗的測試數量,所以可以在最后給出一個總結報告。這就是我們所需要的所有的代碼,如果我們將他們放到一起,就是下面的44行代碼:

module Kernel def describe(description, &block)  tests = Dsl.new.parse(description, block)  tests.execute endendclass Object def should  self endendclass Dsl def initialize  @tests = {} end def parse(description, block)  self.instance_eval(&block)  Executor.new(description, @tests) end def it(description, &block)  @tests[description] = block endendclass Executor def initialize(description, tests)  @description = description  @tests = tests  @success_count = 0  @failure_count = 0 end def execute  puts "#{@description}"  @tests.each_pair do |name, block|   print " - #{name}"   result = self.instance_eval(&block)   result ? @success_count += 1 : @failure_count += 1   puts result ? " SUCCESS" : " FAILURE"  end  summary end def summary  puts "/n#{@tests.keys.size} tests, #{@success_count} success, #{@failure_count} failure" endend

如果我們“需要”使用這個框架執行最初的那個測試,我們會得到下面輸出結果:

    some test

    - should be true SUCCESS

    - should show that an expression can be true SUCCESS

    - should be failing deliberately FAILURE

    3 tests, 2 success, 1 failure

太好了!現在,如果你因沒有一個單元測試框架而煩惱并且不想莽撞地寫代碼,只要花上5分鐘你就可以得到一個能夠助你一臂之力的測試框架。當然,這里有一些略微夸大;你很快就會想到這里缺少額外的驗證API、更好的輸出、對象仿真和測試樁等等。然而,我們可以很容易的在精簡的框架上擴展其中的一些功能(例如,增加額外的DSL元素)――只消花費很小的努力。如果你不相信我,可以看看bacon ,它只用了幾百行代碼就完成了Rspec一個精簡版。我編寫的Attest測試框架是另一個很好的例子(這么說有自賣自夸的嫌疑:P)。這兩者都缺少任何內建的test double 支持,我會在另外一個時間討論如何添加test double支持。

譯注:Test Double:在對象編程中“自動化單元測試”的專業術語,涵蓋的類型有Test Stub(測試樁)、Mock Object、Test Spy、Fake Object和Dummy Object。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91精品国产沙发| 久久99视频精品| 亚洲va欧美va在线观看| 亚洲精品99999| 92国产精品久久久久首页| 91色p视频在线| 法国裸体一区二区| 国产精品丝袜视频| 日韩美女主播视频| 欧美日韩激情美女| 国产欧美va欧美va香蕉在| 欧美精品免费看| 久久久久久久久久av| 色播久久人人爽人人爽人人片视av| 欧美国产在线视频| 欧美一区二三区| 在线精品高清中文字幕| 国产伦精品免费视频| 色偷偷av一区二区三区乱| 色婷婷综合久久久久中文字幕1| 亚洲黄页视频免费观看| 国产免费一区二区三区香蕉精| 大胆人体色综合| 成人黄色短视频在线观看| 国产视频综合在线| 欧美激情第三页| 精品国产自在精品国产浪潮| 精品国偷自产在线视频99| 国产精品白丝jk喷水视频一区| 精品亚洲永久免费精品| 亚洲成人激情小说| 国产欧美婷婷中文| 亚洲男人的天堂网站| 亚洲欧洲在线视频| 久久精品男人天堂| 日韩专区中文字幕| 日韩一区视频在线| 午夜精品久久久久久久白皮肤| 久国内精品在线| 136fldh精品导航福利| 亚洲人成欧美中文字幕| 97超级碰碰碰| 欧美午夜女人视频在线| 国产精品成人在线| 欧美极品少妇xxxxⅹ喷水| 国产日韩中文字幕在线| 懂色av中文一区二区三区天美| 欧美国产日韩一区| 97视频在线播放| 欧美亚洲视频一区二区| 成人午夜在线视频一区| 97久久精品人人澡人人爽缅北| 日韩视频第一页| 亚洲伦理中文字幕| 国产精品久久999| 欧美高清视频在线观看| 亚洲色图第三页| 日本一区二区在线免费播放| 亚洲第一免费网站| 韩剧1988在线观看免费完整版| 国产99久久精品一区二区| 欧美黑人xxx| 日韩免费在线免费观看| 欧美激情视频播放| 日韩欧美国产网站| 亚洲精品国产精品国自产观看浪潮| 日本一区二三区好的精华液| 欧美极品xxxx| 亚洲91精品在线| 国产精品嫩草影院久久久| 精品国产一区二区三区久久久狼| 中文字幕精品一区二区精品| 欧美性受xxxx黑人猛交| 国产精品视频自在线| 在线观看国产精品淫| 亚洲大胆人体在线| 国产午夜精品全部视频播放| 91久久精品在线| 欧美日本中文字幕| 欧美性猛交xxxx乱大交| 久久99精品视频一区97| 国产亚洲精品久久久| 国产精品久久久久999| 欧美在线精品免播放器视频| 亚洲精品日韩激情在线电影| 亚洲一区中文字幕| 国语自产精品视频在免费| 91久久精品国产91性色| 97香蕉久久夜色精品国产| 最新国产成人av网站网址麻豆| 播播国产欧美激情| 亚洲人精品午夜在线观看| 日韩美女中文字幕| 国产精品jvid在线观看蜜臀| 色综合久久中文字幕综合网小说| 精品亚洲国产视频| 久久精品亚洲国产| 亚洲跨种族黑人xxx| 日韩欧美中文免费| 久久久成人精品| 成人av色在线观看| 欧美性理论片在线观看片免费| 88国产精品欧美一区二区三区| 久久这里有精品| 久久露脸国产精品| 亚洲视频电影图片偷拍一区| 亚洲第一精品夜夜躁人人躁| 久久成人亚洲精品| 不卡毛片在线看| 欧美激情视频在线观看| 亚洲高清免费观看高清完整版| 亚洲欧美制服第一页| 国产91在线播放九色快色| 国内外成人免费激情在线视频| 国产狼人综合免费视频| 欧美黄色片在线观看| 欧美黑人巨大精品一区二区| 国产精品久久久久久久9999| 久久久久久久久91| 欧美在线观看一区二区三区| 欧美性猛交xxxx免费看| 欧美中文字幕在线观看| 蜜臀久久99精品久久久无需会员| 91亚洲国产成人精品性色| 国产精品第一区| 色与欲影视天天看综合网| 午夜精品国产精品大乳美女| 91国自产精品中文字幕亚洲| 亚洲精品成人久久| 成人激情视频在线观看| 欧美一区二区三区免费视| 精品久久久久久亚洲国产300| 国产精品久久久久久久久免费看| 亚洲激情 国产| 欧美床上激情在线观看| 成人免费淫片视频软件| 日韩欧美在线国产| 亚洲精品永久免费| 精品国产老师黑色丝袜高跟鞋| 亚洲福利视频免费观看| 亚洲人成在线电影| 亚洲自拍偷拍网址| 91成人免费观看网站| 伊人久久综合97精品| 成人欧美一区二区三区在线湿哒哒| 欧美野外wwwxxx| 91在线免费看网站| 成人免费视频在线观看超级碰| 欧美在线亚洲在线| 欧美另类高清videos| 日本一欧美一欧美一亚洲视频| 亚洲欧美在线免费| 国产免费一区视频观看免费| 欧洲s码亚洲m码精品一区| 久久久久中文字幕2018| 久久久久亚洲精品成人网小说| 亚洲一区国产精品| 2018日韩中文字幕| 日本中文字幕成人| 91精品成人久久| 久久久久中文字幕2018| 亚洲高清一区二| 久久久久久久久久婷婷| 国产精品色午夜在线观看|