本文章是由機器翻譯。

測試回合

.NET 單元測試的 IronPython

James McCaffrey

可從 MSDN 程式庫 的程式碼下載
瀏覽線上的程式碼

內容

測試下,模組
廣告機操作的互動式單元測試
輕量型的模組測試自動化
向上換行

Python 的指令碼語言,提供的讓它執行的軟體測試的數個類型的絕佳選擇的功能。有可包括 CPython 的數個 Python 實作,於晚期 2006 為目標的 Microsoft.NET Framework 發行的最常見的實作執行 UNIX 類似的作業系統和 IronPython,實作的機器上]。在本月的專欄中,我將示範如何使用 IronPython 來測試.NET 架構的模組從兩個 Python 命令列] 和 [輕量型的 Python 指令碼中。我會假設您有一些經驗,使用 JavaScript、 Windows PowerShell、 VBScript、 Perl、 PHP,指令碼語言,或 [注音標示],但我不要假設您有任何的 Python 的經驗。請看一下 [圖 1 ],以了解,我向。我正在使用 IronPython 執行快速、 臨機操作,比較兩個卡手,在.NET 的模組,名為 TwoCardPokerLib 中方法的測試。我會描述命令稍後在這個的資料行中詳細如 [圖 1 ] 所示,但是現在觀察我以互動方式將參考加入 DLL,包含我的.NET 模組、 兩個 Hand 物件,從該模組,具現化和呼叫比較方法來判斷一方面是否問倒了其他手。現在請瞧瞧在 [圖 2 。該螢幕擷取畫面會顯示在執行輕量型的 IronPython 指令碼,花我只分鐘的時間建立。再次,我的所有詳細資料透過稍後,但您可以看到我執行傳統單元測試的測試案例資料中讀取文字檔、 具現化從 TwoCardPokerLib 模組的兩個手物件、 呼叫比較) 方法及進行比較,實際的結果與預期的結果,以判斷測試案例的通過 / 失敗結果。

[圖與命令列上的 IronPython 1 臨測試

>>> import sys
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib']
>>> sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\  Debug')
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib', 'C:\\ModuleTestingWithPython\\  TwoCardPokerLib\\bin\\Debug']
>>>
>>> import clr
>>> dir()
['_', '__builtins__', '__doc__', '__name__', 'clr', 'site', 'sys']
>>>
>>> clr.AddReferenceToFile("TwoCardPokerLib.dll")
>>> from TwoCardPokerLib import *
>>> dir()
['Card', 'Hand', '_', '__builtins__', '__doc__', '__name__', 'clr',   'site', 'sys']
>>>
>>> c1 = Card()
>>> c2 = Card("9d")
>>> print c1,c2
As 9d
>>>
>>> h1 = Hand(c1,c2)
>>> h2 = Hand("Ah","8c")
>>> expected = 1
>>> actual = h1.Compare(h2)
>>>
>>> if actual == expected: print "Pass\n",
... else: print "Fail\n"
...
Pass
>>>
>>> ^Z

C:\IronPython>

fig02.gif

[圖 2 使用的 IronPython 的指令碼的測試自動化

在下列章節中,我將說明 TwoCardPokerLib 類別庫,在 [測試,讓您將瞭解哪些測試的完全。接著我將討論我使用 [圖 1 ] 中的互動式 IronPython 命令。接下來我會呈現,並簡短的 Python 指令碼產生 [圖 2 ] 中的輸出的詳細說明。測試控管] 和 [測試的程式庫在完整原始程式碼位於本專欄所附的下載中。

我會包裝以簡短討論如何調整和擴充此處呈現,以符合您自己的測試的需求的意見。我相信您會發現測試此處介紹的技巧,Python 會是一個好新增技能組合的測試您的軟體。

測試下,模組

現在我們來看測試程式庫。我決定在建立.NET 類別庫進行測試的功能不足,無法顯示當執行實際的模組,在實際執行的環境中測試,但無法在程式庫的詳細資訊遮蔽測試更複雜的問題讓您面對的問題的類型。我 imagined 假設遊戲的兩個卡 Poker 每位玩家從四個,標準、 52 卡 Deck 接收只是兩張牌的位置。這當然會導致使用卡類別、 手類別和比較方法的物件導向設計,以判斷哪個兩個手的物件為更好。我決定每個兩個卡手直接清除 (同花色的牌連續卡)、 一組 (兩個具有相同的陣序卡)、 清除 (兩個同花色的牌卡)、 一個直接 (兩個連續卡,) 會被分類 Ace 高到四個高。請注意三個高手的形狀是不可能因為三個兩個是一個直接且三個 A 高的 A。即使這樣一個簡單的範例是相當有趣的實作的。IronPython 可用來有效地測試.NET 程式庫,不論實作的語言,以及 IronPython 可用於測試傳統的 COM 程式庫。我 TwoCardPokerLib 程式庫中的卡類別原始程式碼會列在 [圖 3 中。

[圖 3 測試的程式庫的卡類別

public class Card
{
  private string rank;
  private string suit;
  public Card() {
    this.rank = "A"; // A,2, . . ,9,T,J,Q,K
    this.suit = "s"; // c,d,h,s
  }
  public Card(string s) {
    this.rank = s[0].ToString();
    this.suit = s[1].ToString();
  }
  public override string ToString() {
    return this.rank + this.suit;
  }
  public string Rank {
    get { return this.rank; }
  }
  public string Suit {
    get { return this.suit; }
  }
  public static bool Beats(Card c1, Card c2) {
    if (c1.rank == "A") {
      if (c2.rank != "A") return true;
      else return false;
    }
    else if (c1.rank == "K") {
      if (c2.rank == "A" || c2.rank == "K") return false;
      else return true;
    }
    else if (c1.rank == "Q") {
      if (c2.rank == "A" || c2.rank == "K" || c2.rank == "Q") return false;
      else return true;
    }
    else if (c1.rank == "J") {
      if (c2.rank == "A" || c2.rank == "K" || c2.rank == "Q" || c2.rank == "J")
        return false;
      else
        return true;
    }
    else if (c1.rank == "T") {
      if (c2.rank == "A" || c2.rank == "K" || c2.rank == "Q" ||
        c2.rank == "J" || c2.rank == "T")
        return false;
      else return true;
    }
    else { // c1.rank is 2, 3, . . 9
      int c1Rank = int.Parse(c1.rank);
      int c2Rank = int.Parse(c2.rank);
      return c1Rank > c2Rank;
    }
  } // Beats()

  public static bool Ties(Card c1, Card c2) {
    return (c1.rank == c2.rank);
  }
} // class Card

為了保持我簡短的程式碼和主要的想法清除,我已您在生產環境例如省略所有的錯誤檢查時不會採取一些捷徑。 我卡類別是刻意設計說明的許多一般的.NET 模組的一般功能。 請注意有預設建構函卡式,建立的 「 黑桃王 」 遊戲卡物件為 A。 沒有建構函式,以接受例如 td 建立物件,表示在 10 片卡的字串。 我實作公開陣序和花色卡物件的 get 屬性。 並我實作靜態打敗] 和 [Ties 方法可以用來判斷是否一個 Card 物件問倒了,或者連結另一個 Card 物件。 我的手類別是太長,無法讓我只會描述金鑰部分類別,在整個所呈現的。 手動類別會有兩個成員欄位:

public class Hand {
  private Card card1;
  private Card card2;
  // constructors, methods, etc.
}

我做重要的假設,card1 是在較高的 (或可能是等於) 兩個介面卡物件。 這可大幅簡化我 hand.Compare 方法的邏輯。 我的手類別有建構函多個式,一般情況下,您必須考慮到執行單元測試時。 預設手動建構函式會使用兩個 Ace–of-「 黑桃王 」 遊戲卡,建立手的形狀:

public Hand() {
  this.card1 = new Card(); 
  this.card2 = new Card();
}

我實作兩個其他建構函手式,可讓我在兩個卡物件中的任一傳遞或兩個字串:

public Hand(Card c1, Card c2) {
  this.card1 = new Card(c1.Rank + c1.Suit);
  this.card2 = new Card(c2.Rank + c2.Suit);
}
public Hand(string s1, string s2) {
  this.card1 = new Card(s1);
  this.card2 = new Card(s2);
}

[圖 4 ] 中我實作四個的私用 Helper 方法。 您將會看到便表示私用方法不會公開給的 IronPython 測試指令碼。

[圖 4 私用方法

private bool IsPair() { return this.card1.Rank == this.card2.Rank; }
private bool IsFlush() { return this.card1.Suit == this.card2.Suit; }
private bool IsStraight() {
  if (this.card1.Rank == "A" && this.card2.Rank == "K") return true;
  else if (this.card1.Rank == "K" && this.card2.Rank == "Q") return true;
  // etc: Q-J, J-T, T-9, 9-8, 8-7, 7-6, 6-5, 5-4, 4-3, 3-2
  else if (this.card1.Rank == "A" && this.card2.Rank == "2") return true; 
  else return false;
}
private bool IsStraightFlush() { return this.IsStraight() &&
  this.IsFlush();
}

為一般法則時執行單元測試,您無法明確地測試私用方法,。 其概念是 Helper 方法中的任何錯誤將會公開測試公用方法,使用協助程式時)。 hand.Compare) 方法會令人驚訝的是複雜的。 我撰寫私密打敗和繫結的 Helper 方法,並使用實作公用的比較方法:

public int Compare(Hand h) {
  if (this.Beats(h))
    return 1;
  else if (this.Ties(h))
    return 0;
  else if (h.Beats(this))
    return -1;
  else
    throw new Exception("Illegal path in Compare()");
}

我會使用舊的 C 語言 strcmp(s,t) 函式架構 hand.compare—if 「 左的手參數 (「 這個 」 物件),問倒了 「 對 」 的參數 (在明確的手輸入參數),比較傳回 1。 如果在正確的參數問倒了左的參數比較就會傳回-1。 如果兩個手物件相等的 (根據我的規則,則比較會傳回 0。 [圖 5 ] 所示,私用打敗方法是工作的大部分。

圖 5]: 打敗方法

private bool Beats(Hand h) {
  if (this.IsStraightFlush()) {
    if (!h.IsStraightFlush()) return true;
    if (h.IsStraightFlush()) {
      if (Card.Beats(this.card1, h.card1)) return true;
      else return false;
    }
  } // this.IsStraightFlush()
  else if (this.IsPair())
    // code  
  else if (this.IsFlush())
   //  code
  else if (this.IsStraight())
    // code    
  else 
    // code for Ace-high down to Four-high
  }

  return false;
}

打敗的程式碼就是一個長的頁面,並您可以查看 [詳細資料] 如果您想要藉由檢查隨附的程式碼下載。 我故意插入邏輯錯誤,私密 Ties 方法:

else if (this.IsFlush() && h.IsFlush() &&  
  this.card1.Rank == h.card1.Rank)          // error
    return true;

正在比較的兩個手物件是否同時清除然後我會檢查如果兩個手中有高卡的相同陣序。 但是我不要請檢查每一個手的第二個卡,我應該有:

else if (this.IsFlush() && h.IsFlush() &&  
  this.card1.Rank == h.card1.Rank &&
  this.card2.Rank == h.card2.Rank)         // correct
    return true;

此邏輯錯誤會產生 [圖 2 ] 所示,測試案例失敗。 我建置我的程式庫使用 Visual Studio 建立類別庫專案,名為 TwoCardPokerLib C:\Module TestingWithPython,在 C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug TwoCardPokerLib.dll 檔案產生。

廣告機操作的互動式單元測試

現在讓我們看看如何檢查並測試使用 IronPython 的.NET 程式庫。 尤其,讓我們查看每個 [圖 1 ] 中,螢幕擷取畫面中顯示的命令。 在第一個的部分,如 [圖 1 ] 所示的輸出的會指出我使用 IronPython 1.1.1 版。

IronPython 會是從 CodePlex,Microsoft 贊助開放原始碼專案在 codeplex.com/IronPython 免費下載。 它需要.NET Framework 2.0,並在支援的版本,Framework 的任何電腦上執行。 我使用 Windows Vista。 您也不真正安裝 [IronPython,而您下載單一壓縮的檔案到您的電腦並擷取任何方便的目錄內容。 在我的情況下我預存 IronPython 的檔案和子目錄,在 C:\IronPython。 我叫用 Python 命令列解譯器 (ipy.exe),並指定選擇性-X: TabCompletion 引數,以索引標籤的完成。 如果您輸入 ipy.exe-h,您會取得所有選項的清單。 設定 IronPython 時它將會執行特殊的啟動指令碼,名為在 site.py,如果這類的指令碼的存在。 我可以使用隨附 IronPython,不進行任何修改標準的 site.py 檔案。

IronPython 初始化之後,我就會檢查目前的 IronPython 系統路徑資訊:

>>> import sys
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib']

我第一次發出匯入 sys 命令,需要特別的 IronPython sys 模組內方法的存取。 接下來我要顯示路徑,尋找不在目前目錄中的檔案時,IronPython 會檢查的清單。 您可以視為的本機的 IronPython 的機制,有點類似 Windows 作業系統路徑的環境變數。 因為我 IronPython 以叫用 「 TabCompletion 」 功能如果我想要知道哪些屬性和方法是使用我從 sys 模組,我無法輸入 sys。 並重複地叫用 <tab> 金鑰。 接下來我告訴 IronPython 我的.NET 的單元測試所在的位置:

>>> sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug')
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib', 'C:\\ModuleTestingWithPython\\TwoCardPokerLib\\bin\\Debug']

我可以使用 sys 模組的 path.Append 方法將新的目錄新增到 IronPython 搜尋清單中。 請注意 r 的字元前,將附加的字串引數。 Python 中,您可以使用 [單引號] 或 [字串周圍的雙引號。 不過,與某些語言,不同的是 Python 會評估逸出字元 (例如 n 在單一引號和雙引號括住的字串。 請確定字串是嚴格做為常值 (Python 術語中的 「 原始字串) 您可以使用 r 修飾詞為我已完成上述。 之後附加新的目錄中,我確認我沒有製作任何輸入錯誤發出 sys.path 命令。 接下來,我準備測試載入 [我的 DLL,由第一個啟用方法,可以載入.NET 模組:

>>> import clr
>>> dir()
['_', '__builtins__', '__doc__', '__name__', 'clr', 'site', 'sys']

我在發出匯入 clr (為一般的 [Language] Runtime 中) 命令並現在我存取至 clr 模組,具有可以載入.NET 組件的數個方法。 然後我會使用 Dir() Python 命令以查看哪些模組我目前具有存取權。 請注意我做不目前有存取 TwoCardPokerLib 程式庫。 現在我可以使用 clr 模組存取我的程式庫進行測試:

>>> clr.AddReferenceToFile("TwoCardPokerLib.dll")
>>> from TwoCardPokerLib import *
>>> dir()
['Card', 'Hand', '_', '__builtins__', '__doc__', '__name__', 'clr', 'site', 'sys']

我使用 AddReferenceToFile 方法,以啟用我目前的 IronPython 環境,以呼叫 TwoCardPokerLib DLL。 Python 是區分大小寫的因此如果我必須輸入 addreferencetofile,例如,我會已發現一個無例如-屬性 (方法) 的錯誤中。

我測試的模組參考加入之後,我需要使用匯入陳述式來使用模組。 我可以使用具型別的匯入 TwoCardPokerLib,但是我從 TwoCardPokerLib 匯入所輸入 * 而。 如果第一個簡單的表單的使用我必須完整限定我模組,例如,TwoCardPokerLb.hand 代替只鍵入手中的所有項目。 [匯入] 命令從-<module>-匯入-<classes> 形式可讓我省略父模組名稱,當我鍵入時。 請注意我做 Dir() 命令之後,我看見 TwoCardPokerLib 模組內,卡] 和 [手的類別是現在可用。 現在,我可以藉由建立兩個卡物件練習我的單元測試:

>>> c1 = Card()
>>> c2 = Card("9d")
>>> print c1,c2
As 9d

我會使用預設卡建構函式以產生名為 c1 的物件。 回想一下前, 一節中預設卡建構函式會建立 「 黑桃王 」 遊戲物件的一個 A。 我使用非預設的卡建構函式建立的物件,表示在九個菱形控點。 請注意我不具現化物件,像我一樣在某些語言中使用關鍵字,例如 [新增]。 如果我必須使用簡短"import TwoCardPokerLib"陳述式之前,我就已經呼叫建構函式為 「 c1 = TwoCardPokerLib.card() 」。 我使用內建 Python 列印陳述式,以顯示 [我的兩個卡物件。 在幕後 IronPython print 陳述式實際上呼叫 card.ToString() 方法我在我的類別庫中實作。 現在我會具現化手的兩個物件,並呼叫 hand.Compare 方法:

>>> h1 = Hand(c1,c2)
>>> h2 = Hand("Ah","8c")
>>> expected = 1
>>> actual = h1.Compare(h2)  

我會將剛建立的兩個卡物件) 傳遞到建立在第一個的手在手動建構函式,然後再我使用的建構函式參數的字串版本具現化的第二個手的形狀中。 因為手的形狀的九個 A-問倒了手的形狀的 A-8,我會希望比較方法傳回 1,所以我將該值儲存在呼叫必須是變數。 請注意 Python 是一種動態型別的語言,因此我不宣告資料型別。 我呼叫 hand.Compare 方法和儲存結果到名為變數實際。 我可以手動類別中輸入手動檢視可用的方法。 在命令列和再打 <tab> 金鑰。 我會看到公用方法,例如比較,且 ToString,不過我會不會看到私密的方法,例如打敗和 Ties。 現在我可以判斷通過 / 失敗結果,我臨機操作的互動式測試:

>>> if actual == expected: print "Pass\n",
... else: print "Fail\n"
...
Pass
>>>

Python If-Then 語法,在命令列是有點不尋常的因此我會在下一節中說明它中。 但基本上是我可以檢查呼叫實際在變數中的值等於在呼叫變數的值必須是。 如果,我就會顯示 「 傳遞訊息 ; 否則我列印失敗 」)。 請注意,我已包含新行字元,在每個的字串。 中繼...」 回應從 IronPython 解譯器表示我有一個不完整的命令,解譯器正在等待我完成命令。

輕量型的模組測試自動化

現在看看如何撰寫輕量型的 IronPython 指令碼,以測試.NET 類別庫。 [圖 6 ] 中的指令碼會產生 [圖 2 ] 所示的輸出。

[圖 6 檔案 harness.py

# harness.py
# test TwoCardPokerLib.dll using data in TestCases.txt

print "\nBegin test run\n"
import sys
print "Adding location of TwoCardPokerLib.dll to sys.path"
sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug')

import clr
print "Loading TwoCardPokerLib.dll\n"
clr.AddReferenceToFile("TwoCardPokerLib.dll")
from TwoCardPokerLib import *

print "=================================="
fin = open("TestCases.txt", "r")
for line in fin:
  if line.startswith("$"):
    continue
  (caseID,input,method,expected,comment) = line.split(':')
  expected = int(expected)
  (left,right) = input.split(',')
  h1 = Hand(left[0:2],left[2:4])
  h2 = Hand(right[0:2],right[2:4])
  print "Case ID = " + caseID
  print "Hand1 is " + h1.ToString() + "   " + "Hand2 is " + h2.ToString()
  print "Method = " + method + "()"
  actual = h1.Compare(h2)

  print "Expected = " + str(expected) + " " + "Actual = " + str(actual)
  if actual == expected:
    print "Pass"
  else:
    print "** FAIL **"


  print "=================================="

fin.close()
print "\nEnd test run"
# end script

我隆 l 我 IronPython 測試控管指令碼,以註解:

# harness.py
# test TwoCardPokerLib.dll using data in TestCases.txt

# 會是 Python 的指令碼的 [註解] 字元。 Python 是線型的因此執行行結尾註解。 Python 也支援多行註解,使用三重單引號。 Python 的指令碼使用.py 的副檔名,而且可以使用包括 「 記事本 」,任何文字編輯器建立。 我的測試控管會從名為 TestCases.txt 外部檔案,以讀取測試案例資料:

0001:AcKc,AdAs:Compare:1:  "royal flush" > pair Aces
0002:Td9d,Th9h:Compare:0:  straight flush diamonds == straight flush hearts
$0003:Ah2c,As9c:Compare:1:   straight > Ace high
0004:9h6h,9d7d:Compare:-1:  flush 9-6 high < flush 9-7
0005:KcJh,As5d:Compare:-1: King high < Ace high

我在這裡有只是五個的測試案例,不過在實際執行的案例中,我會有數百或數千。 有 7,311,616 (或 524 的) 來說明的徹底測試更簡單的模組,impracticality 的 hand.compare 合法的輸入。

每一行都代表單一的測試案例。 每個欄位是以冒號字元分隔的。 第一個欄位是一個測試案例的 ID。 第二個欄位會是字串,包含以逗號字元分隔的兩個手物件的資訊。 第三個欄位會指示要測試的方法。 第四個欄位會是預期的值。 第五個欄位會是選擇性,並且是在測試案例的註解。 請注意測試案例 0003 被加上 $ 字元。 我將掃描的 [我的測試控管的這個字元,並略過任何這類的測試案例。 測試案例 0004 產生失敗結果因為蓄意的邏輯錯誤 (僅供示範之用),我在 hand.ties 方法,由測試 hand.Compare 方法呼叫內放置。 在文字檔中,儲存測試案例資料時,是快速且容易。 我會無法也具有內嵌我的測試案例資料直接將我的控管,或儲存我的情況下的 XML 檔案,等等。 Python 可以輕鬆地處理任何測試案例的儲存配置。 接下來,我會顯示訊息到命令殼層:

print "\nBegin test run\n"

因為 Python 字串可以以雙引號或單引號中, 分隔,在兩個引號樣式評估逸出字元,引號型別之間選擇實際上是的喜好設定。 我通常使用雙引號以外當我將使用 / r 修飾詞,使我的字串常值 (Literal),在這種情況下我傾向使用單引號。 下一個我的 Python 測試控管的指令碼會將測試我的 DLL 的位置加入至 Python 系統路徑中:

import sys
print "Adding location of TwoCardPokerLib.dll to sys.path"
sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug')

此處我硬編碼測試 DLL 的位置。 我可以將命令列的引數傳遞至 [我的 Python 的指令碼,並使用內建的 sys.argv 陣列存取它們。 例如,如果我要叫用 [我的指令碼為

  > ipy.exe   harness.py   C:\Data   MyLib.dll

然後 sys.argv[0] 會保留我的指令碼 (harness.py) 的名稱 sys.argv[1] 會儲存,在第一個引數 (C:\ Data)] 和 [sys.argv[2] 會保留第二個引數 (MyLib.dll)。 接下來我會告訴我控管,來載入我的單元測試:

import clr
print "Loading TwoCardPokerLib.dll\n"
clr.AddReferenceToFile("TwoCardPokerLib.dll")
from TwoCardPokerLib import *

方法有很多載入使用 IronPython 的.NET 程式庫。 當您 sys.path 包含加入,程式庫的位置,但 clr 模組,也包含包括 clr.AddReference、 clr.AddReferenceByName、 clr.AddReferenceByPartialName 和 clr.AddReferenceToFileAndPath 的替代方案,,clr.AddReferenceToFile 方法會會簡單且有效。 我在這裡使用,* 萬用字元,為從 TwoCardPokerLib 媒體櫃,載入所有的類別,但我可以命名它們來載入特定的類別。 Python 會有幾種方式處理一個文字檔案逐行。 我會使用內建的檔案開啟函式,並將它傳遞的名稱 [我的測試案例檔案和指示我正在開啟檔案供讀取的 r 引數:

print "=================================="
fin = open("TestCases.txt", "r")
for line in fin:
  # process line here

其他常見的模式包括 w 寫入和附加。 模式引數是選用的預設值為 / r,所以我可能已經省略它。 在 Docs.Python.org,可以找到好 Python 語言功能、 語法和內建函式參考。 開啟函式的結果會是我指派給變數名為 Fin 檔案控制代碼。 然後我使用一個 For 迴圈,逐一查看以行的行。 我命名為我行儲存變數"line",但我無法使用任何合法的變數名稱。 請注意在語法的 Python 怪癖: 而非使用開始結束語彙基元,例如 {。 .. } 或開始。 .. 結束,大部分的語言不,Python 使用冒號字元縮排的搭配指出開始,並在完成陳述式區塊。 在 [我的主要處理迴圈中,,我會使用內建 startswith 字串函式來檢查目前的行從 [我的測試案例檔案是否是以錢幣符號字元開頭:

if line.startswith("$"):
  continue

如果我發現 $,我會使用繼續陳述式來略過中的剩餘陳述式在迴圈,] 和 [下一個行從 [我的測試案例檔案的讀取。 Python 都有迴圈並決定控制結構的一完成組。 接下來我會剖析我的測試案例資料行至其個別的欄位:

(caseID,input,method,expected,comment) = line.split(':')
expected = int(expected)

我使用稱為有序元組和內建的分割字串函式的 Python 的一個很棒的功能來快速剖析我的資料時。 我無法執行使用傳統的陣列的方式,您會看到以下,但是我認為,Python 的有序元組方法是較短和容易瞭解的工作。

tokens = line.split(':')
caseID = tokens[0]
input = tokens[1]
method = tokens[2]
expected = tokens[3]
comment = tokens[4]

我使用 Int() 函式明確轉換稱為預期,從型別的字串,讓我可以比較型別 int 變數與實際的預期。 其他有用的型別轉換函式會包括 Str() 」、 「 float() 」、 「 long() 和 「 bool()。 接下來我還會執行第二個的剖析作業,以擷取兩個手中輸入:

(left,right) = input.split(',')
h1 = Hand(left[0:2],left[2:4])
h2 = Hand(right[0:2],right[2:4])

我使用分割的第一個剖析之後, 變數的輸入會保留像 KcJh,As5d 字串值。 所以,我呼叫重新分割,以,引數] 和 [到變數,我呼叫,左和右中儲存兩個的結果。 現在稱為左字串變數中保留 KcJh,並權限會保留 As5d。 與許多的語言不同 Python 沒有一個子字串的方法。 而,Python 使用陣列的索引來擷取子字串。 因此如果左邊的變數會保存 KcJh 然後由運算式左陣列索引開始,在 0,[0: 2] yields Kc 和運算式會保留 [2:4] yields Jh。 請注意從較大的字串中,擷取子字串,您指定之第一個字元,索引較大的字串 (如會預期) 但其中一個比結束的字元索引。 一旦我有字串,表示個別卡,我會傳遞它們至手動建構函式。 接下來我會回應殼層我輸入的資料:

print "Case ID = " + caseID
print "Hand1 is " + h1.ToString() + "   " + "Hand2 is " + h2.ToString()
print "Method = " + method + "()"

Python 使用 「 + 執行字串串連,以便在上面,三個列印陳述式中,我正在列印三個字串的字元。 列印函式可以接受多個引數隔開,字元,因此我無法寫入陳述式,例如下列以產生相同的輸出:

print "Case ID = " , caseID

現在我準備要呼叫測試方法:

actual = h1.Compare(h2)
print "Expected = " + str(expected) + " " + "Actual = " + str(actual)

請注意因為我實作的執行個體方法,hand.Compare 方法,我呼叫它從 h1 手物件的內容。 如果我必須編碼為靜態方法的比較我會有呼叫它就像這樣:

actual = Compare(h1,h2)

請注意此時實際與預期的變數型別為 int 的因為實際是傳回值這個從比較方法,定義要傳回的 int,而且必須是值已明確轉型成整數 因此我轉換實際和預期的字串,讓我可以串連為單一輸出字串。 現在我還會顯示 [我的測試案例的通過 / 失敗結果:

if actual == expected:
  print "Pass"
else:
  print "** FAIL **"

print "=================================="

Python 使用縮排,以指示 [開始] 和 [區塊的陳述式的結尾。 如果您是在其他語言有經驗的程式設計的許多,似乎有點奇怪的 Python 使用縮排第一次。 但是,我要說 Python 的不尋常的語法問題變得熟悉相當快速曾大部分軟體測試人員。

如果我要追蹤的通過的測試案例的總數,我可以初始化計數器 [我的主要處理迴圈外就像這樣:

numPass = numFail = 0

然後再修改我的 If-Then 結構:

if actual == expected:
  numPass += 1
  print "Pass"
else:
  numFail += 1
  print "** FAIL **"

然後,我可以列印我在處理迴圈外的結果:

print "Num pass = " , numPass ,  " num fail = " ,  numFail

請注意 Python 不支援像是 numPass ++ 的語法或 ++ numPass 遞增的 int 變數。 此處我只列印命令殼層中,結果,但我可以輕易地結果寫入文字檔、 XML 檔案、 SQL 資料庫或其他存放裝置]。 現在我將我的測試控管完成設定:

fin.close()
print "\nEnd test run"
# end script

我會關閉 [我的測試大小寫的檔案參考釋放其檔案,控制代碼和列印訊息,指出測試已完成。 我的指令碼結構是非常簡單,但 Python 具有的功能,可以讓您管理複雜的指令碼]。 例如,在實際執行環境中我絕對會包裝在例外狀況處理常式中的整個指令碼:

try
  # harness code here
except
  # handle any exceptions here 

此外,Python 會支援程式設計人員定義的函式,因此我無法有化我的控管 main 函式加上 Helper 函式。

向上換行

自動化的單元測試是視為最基本的軟體測試自動化類型。 每當我要評估測試自動化的程式設計語言的適用性,我會先檢查語言如何處理單元測試。 我認為,IronPython 與 flying colors 通過這個簡易測試]。 沒有程式設計語言是所有的測試工作和案例。 換句話說,IronPython 會具有,讓您的絕佳的選擇,做為單元測試的語言的許多功能。

藉由使用 IronPython 的自動化單元測試與如何輕量型的模組測試的一提,可讓我所完成。 單元測試架構,例如 NUnit 通常會放置直接在您的模組程式碼中。 測試導向的開發與單元測試會是主要的開發人員活動。 使用單元測試方法,在開發期間不 absolve 您從執行徹底負責,專用單元測試。 這是 Python 的時候。 也就是說單元測試與 Python 是對一個補充,不進行單元測試替代。 這兩種方法一起使用時,您可以產生更好、 更可靠的軟體。

您提出問題或意見,請至 James 寄 testrun@Microsoft.com.

Dr.James McCaffrey 適用於 Volt Information Sciences Inc,.,他,管理在 Microsoft 所使用的軟體工程師的技術訓練。 他有工作在數個 Microsoft 產品,包括 Internet Explorer 和 MSN 搜尋。 詹姆士會是 .NET Test Automation Recipes 的作者。 在達到他 jmccaffrey@volt.comv-jammc@Microsoft.com.