主頁(yè) > 知識(shí)庫(kù) > 用Ruby實(shí)現(xiàn)一個(gè)單元測(cè)試框架的教程

用Ruby實(shí)現(xiàn)一個(gè)單元測(cè)試框架的教程

熱門標(biāo)簽:天津外呼系統(tǒng)怎么收費(fèi) 征服者企業(yè)地圖標(biāo)注 柯城手機(jī)地圖如何做地圖標(biāo)注 漯河電銷 巫師3地圖標(biāo)注魔力之所 淮安自動(dòng)外呼系統(tǒng)供應(yīng)商 外呼線路從哪里出來(lái)的 AI電銷機(jī)器人 線路 中牟外呼系統(tǒng)違法嗎

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

一個(gè)最小可用的單元測(cè)試

剛開始寫一個(gè)單元測(cè)試框架的時(shí)候代碼是很少的,但當(dāng)我想給它加入一些特性時(shí)就變得沒(méi)有那么精煉了:) 幸運(yùn)的是重寫是很容易的。我們真正需要做的是執(zhí)行下面的代碼:
 

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
 end
end

正如你看到的,它很像是一個(gè)基本的Rspec測(cè)試。讓我們寫一些代碼來(lái)執(zhí)行它。

譯注:RSpec 工具是一個(gè) Ruby 軟件包,可以用它構(gòu)建有關(guān)您的軟件的規(guī)范。該規(guī)范實(shí)際上是一個(gè)描述系統(tǒng)行為的測(cè)試。

構(gòu)建一個(gè)簡(jiǎn)單的框架

首先要做的是使用“describe”來(lái)定義一個(gè)新的測(cè)試。既然我們想要把”describe” block放在任何地方(例如,文件本身),我們需要對(duì)Ruby做一點(diǎn)擴(kuò)展。“puts”函數(shù)在Kernel block中,因此可以在任何地方使用(因?yàn)镺bject類包含了Kernel并且Ruby中的每個(gè)對(duì)象都繼承自O(shè)bject類),同樣的我們會(huì)把describe放到Kernel block中以賦予同樣的能力):
 

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

譯注:Ruby block:Ruby語(yǔ)言的block功能類似回調(diào)函數(shù)。

正如你看到的,”describe”接收一個(gè)用來(lái)描述測(cè)試的字符串和包含了測(cè)試代碼的block。在這里,我們將測(cè)試的代碼和”describe”分開講解(例如,”it” block)。因此我們創(chuàng)建了Dsl類,用它的parse函數(shù)處理待測(cè)試的block,結(jié)果會(huì)產(chǎn)生一個(gè)可以執(zhí)行我們所有測(cè)試的對(duì)象,但是不要高興得太早。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
 end
end

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

self.instance_eval(block)

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

在describe block中,”it”函數(shù)會(huì)為每個(gè)”it” block調(diào)用一次。每次調(diào)用時(shí),會(huì)把輸入的block以測(cè)試描述作為鍵值存儲(chǔ)在哈希表中。完成這些以后,我們只要?jiǎng)?chuàng)建一個(gè)Executor對(duì)象,可以對(duì)我們所有的測(cè)試block進(jìn)行迭代,調(diào)用它們并產(chǎn)生執(zhí)行結(jié)果。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"
 end
end

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

def should_be_five(x)
 5 == x
end

這個(gè)函數(shù)同樣可以在”it” block內(nèi)部使用,但對(duì)于我們這個(gè)簡(jiǎn)單的測(cè)試沒(méi)有這個(gè)必要。

所以,”it” block會(huì)計(jì)算并存儲(chǔ)結(jié)果,通常結(jié)果只是”it” block最后一個(gè)語(yǔ)句的返回值(按照常規(guī)的Ruby)。這里,我們希望確保最后一個(gè)語(yǔ)句總是返回一個(gè)布爾值(標(biāo)明測(cè)試通過(guò)或失?。ㄟ^(guò)它我們可以輸出一些有意義提示。

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

true.should == true
5.should == 5

每個(gè)對(duì)象都應(yīng)當(dāng)提供自己”should”函數(shù),代碼如下:
 

class Object
 def should
  self
 end
end

這個(gè)函數(shù)并沒(méi)有真正做什么工作(僅僅是返回對(duì)象本身);它僅僅是一個(gè)讓測(cè)試讀起來(lái)更好的語(yǔ)法。

在這個(gè)階段,我們只是將測(cè)試計(jì)算的結(jié)構(gòu)轉(zhuǎn)換成一個(gè)字符串,表明測(cè)試結(jié)果通過(guò)或失敗并輸出。在這個(gè)過(guò)程中,我們會(huì)統(tǒng)計(jì)通過(guò)或失敗的測(cè)試數(shù)量,所以可以在最后給出一個(gè)總結(jié)報(bào)告。這就是我們所需要的所有的代碼,如果我們將他們放到一起,就是下面的44行代碼:

module Kernel
 def describe(description, block)
  tests = Dsl.new.parse(description, block)
  tests.execute
 end
end
class Object
 def should
  self
 end
end
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
 end
end
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"
 end
end

如果我們“需要”使用這個(gè)框架執(zhí)行最初的那個(gè)測(cè)試,我們會(huì)得到下面輸出結(jié)果:

    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

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

譯注:Test Double:在對(duì)象編程中“自動(dòng)化單元測(cè)試”的專業(yè)術(shù)語(yǔ),涵蓋的類型有Test Stub(測(cè)試樁)、Mock Object、Test Spy、Fake Object和Dummy Object。

您可能感興趣的文章:
  • Ruby中的Mechanize的使用教程
  • Ruby中區(qū)分運(yùn)行來(lái)源的方法
  • Ruby中檢測(cè)Gem是否安裝的方法

標(biāo)簽:棗莊 南昌 內(nèi)江 河池 克拉瑪依 甘孜 西雙版納 大慶

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《用Ruby實(shí)現(xiàn)一個(gè)單元測(cè)試框架的教程》,本文關(guān)鍵詞  用,Ruby,實(shí)現(xiàn),一個(gè),單元,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《用Ruby實(shí)現(xiàn)一個(gè)單元測(cè)試框架的教程》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于用Ruby實(shí)現(xiàn)一個(gè)單元測(cè)試框架的教程的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章