天天看點

使用 Bash shell 腳本進行功能測試

建立腳本

功能測試是軟體開發的一個關鍵部分 -- 而已經裝入 Linux 的 Bash 可以幫您輕而易舉地完成功能測試。在本文中,Angel Rivera 将說明如何運用 Bash shell 腳本通過行指令來執行 Linux 應用程式的功能測試。由于此腳本依賴于指令行的傳回碼,因而您不能将這種方法運用于 GUI 應用程式。

功能測試是開發周期的一個階段,在這個階段中将測試軟體應用程式以確定軟體的函數如預期的那樣,同時能正确處理代碼中錯誤。此項工作通常在單個子產品的單元測試結束之後,在負載/重壓條件下整個産品的系統測試之前進行的。

市場上有許多測試工具提供了有助于功能測試的功能。然而,首先要擷取它們,然後再安裝、配置,這将占用您寶貴的時間和精力。Bash 可以幫您免去這些煩瑣的事進而可以加快測試的程序。

使用 Bash shell 腳本進行功能測試的優點在于:

Bash shell 腳本已經在 Linux 系統中安裝和配置好了。不必再花時間準備它。

可以使用由 Linux 提供的文本編輯器如 vi 建立和修改 Bash shell 腳本。不需要再為建立測試程式而擷取專門的工具。

如果已經知道了如何開發 Bourne 或 Korn shell 腳本,那對于如何運用 Bash shell 腳本已經足夠了。對您來說,學習曲線已不存在了。

Bash shell 提供了大量的程式設計構造用于開發從非常簡單到中等複雜的腳本。

将腳本從 Korn 移植到 Bash 時的建議

如果已有現成的 Korn shell 腳本,而想要将它們移植到 Bash,就需要考慮下列情況:

Korn 的 "print" 指令在 Bash 中不能使用;而是改為使用 "echo" 指令。

需要将腳本的第一行:

#!/usr/bin/ksh

修改成:

#!/bin/bash

建立 Bash shell 腳本進行功能測試

這些基本的步驟和建議适用于許多在 Linux 上運作的客戶機/伺服器應用程式。

記錄運作腳本的先決條件和主要步驟

将操作分成若幹個邏輯組

基于一般方案制定執行步驟

在每個 shell 腳本中提供注釋和說明

做一個初始備份以建立基準線

檢查輸入參數和環境變量

嘗試提供 "usuage" 回報

嘗試提供一個"安靜"的運作模式

當出現錯誤時,提供一個函數終止腳本

如可能,提供可以執行單個任務的函數

當顯示正在生成的輸出時,捕獲每個腳本的輸出

在每個腳本内,捕獲每個行指令的傳回碼

計算失敗事務的次數

在輸出檔案中,突出顯示錯誤消息,以便于辨別

如有可能,"實時"生成檔案

在執行腳本的過程中提供回報

提供腳本執行的摘要

提供一個容易解釋的輸出檔案

如有可能,提供清除腳本及傳回基準線的方法

下面詳細講述了每一條建議以及用于說明問題的腳本。若要下載下傳此腳本,請參閱本文後面的參考資料部分。

1. 記錄運作腳本的先決條件和主要步驟

記錄,尤其是以有自述标題的單個檔案(例如 "README-testing.txt")記錄功能測試的主要想法是很重要的,包括,如先決條件、伺服器和客戶機的設定、腳本遵循的整個(或詳細的)步驟、如何檢查腳本的成功/失敗、如何執行清除和重新啟動測試。

2. 将操作分成若幹個邏輯組

如果僅僅執行數量非常少的操作,可以将它們全部放在一個簡單的 shell 腳本中。

但是,如果需要執行一些數量很多的操作,那最好是将它們分成若幹個邏輯集合,例如将一些伺服器操作放在一個檔案而将客戶機操作放在在另一個檔案中。通過這種方法,劃分适當的顆粒度來執行測試和維護測試。

3. 基于一般方案制定執行步驟

一旦決定對操作進行分組,需要根據一般方案考慮執行操作的步驟。此想法是模拟實際生活中最終使用者的情形。作為一個總體原則,隻需集中測試 80% 最常調用函數的 20% 用法即可。

例如,假設應用程式要求 3 個測試組以某個特定的順序排列。每個測試組可以放在一個帶有自我描述檔案名(如果可能)的檔案中,并用号碼來幫助識别每個檔案的順序,例如:

[code]1. fvt-setup-1: To perform initial setup.

2. fvt-server-2: To perform server commands.

3. fvt-client-3: To perform client commands.

4. fvt-cleanup: To cleanup the temporary files,

in order to prepare for the repetition

of the above test cases.[/code]

4. 在每個 shell 腳本中提供注釋和說明

在每個 shell 腳本的頭檔案中提供相關的注釋和說明是一個良好的編碼習慣。這樣的話,當另一個測試者運作該腳本時,測試者就能清楚地了解每個腳本中測試的範圍、所有先決條件和警告。

下面是一個 Bash 腳本 "test-bucket-1" 的示例 。

[code]#!/bin/bash

#

# Name: test-bucket-1

#

# Purpose:

# Performs the test-bucket number 1 for Product X.

# (Actually, this is a sample shell script,

# which invokes some system commands

# to illustrate how to construct a Bash script)

#

# Notes:

# 1) The environment variable TEST_VAR must be set

# (as an example).

# 2) To invoke this shell script and redirect standard

# output and standard error to a file (such as

# test-bucket-1.out) do the following (the -s flag

# is "silent mode" to avoid prompts to the user):

#

# ./test-bucket-1 -s 2>&1 | tee test-bucket-1.out

#

# Return codes:

# 0 = All commands were successful

# 1 = At least one command failed, see the output file

# and search for the keyword "ERROR".

#

########################################################[/code]

5. 做一個初始備份以建立基準線

您可能需要多次執行功能測試。第一次運作它時,也許會找到腳本或程序中的一些錯誤。因而,為了避免因從頭重新建立伺服器環境而浪費大量時間 -- 特别是如果涉及到資料庫 -- 您在測試之前或許想做個備份。

在運作完功能測試之後,就可以從備份中恢複伺服器了,同時也為下一輪測試做好了準備。

6. 檢查輸入參數和環境變量

最好校驗一下輸入參數,并檢查環境變量是否設定正确。如果有問題,顯示問題的原因及其修複方法,然後終止腳本。

當測試者準備運作腳本,而此時如果沒有正确設定腳本所調用的環境變量,但由于發現及時,終止了腳本,那測試者會相當感謝。沒有人喜歡等待腳本執行了很久卻發現沒有正确設定變量。

[code]# --------------------------------------------

# Main routine for performing the test bucket

# --------------------------------------------

CALLER=`basename $0` # The Caller name

SILENT="no" # User wants prompts

let "errorCounter = 0"

# ----------------------------------

# Handle keyword parameters (flags).

# ----------------------------------

# For more sophisticated usage of getopt in Linux,

# see the samples file: /usr/lib/getopt/parse.bash

TEMP=`getopt hs $*`

if [ $? != 0 ]

then

echo "$CALLER: Unknown flag(s)"

usage

fi

# Note quotes around `$TEMP': they are essential!

eval set -- "$TEMP"

while true

do

case "$1" in

-h) usage "HELP"; shift;; # Help requested

-s) SILENT="yes"; shift;; # Prompt not needed

--) shift ; break ;;

*) echo "Internal error!" ; exit 1 ;;

esac

done

# ------------------------------------------------

# The following environment variables must be set

# ------------------------------------------------

if [ -z "$TEST_VAR" ]

then

echo "Environment variable TEST_VAR is not set."

usage

fi[/code]

關于此腳本的說明如下:

使用語句 CALLER=`basename $0` 可以得到正在運作的腳本名稱。這樣的話,無須在腳本中寫死腳本名稱。是以當複制腳本時,采用新派生的腳本可以減少工作量。

調用腳本時,語句 TEMP=`getopt hs $*` 用于得到輸入變量(例如 -h 代表幫助,-s 代表安靜模式)。

語 句 [ -z "$X" ] 和 echo "The environment variable X is not set." 以及 usage 都是用于檢測字元串是否為空 (-z),如果為空,随後就執行 echo 語句以顯示未設定字元串并調用下面要讨論的 "usage" 函數。

若腳本未使用标志,可以使用變量 "$#",它可以傳回正在傳遞到腳本的變量數量。

7. 嘗試提供"usage"回報

腳本中使用 "usage" 語句是個好主意,它用來說明如何使用腳本。

[code]# ----------------------------

# Subroutine to echo the usage

# ----------------------------

usage()

{

echo "USAGE: $CALLER [-h] [-s]"

echo "WHERE: -h = help "

echo " -s = silent (no prompts)"

echo "PREREQUISITES:"

echo "* The environment variable TEST_VAR must be set,"

echo "* such as: "

echo " export TEST_VAR=1"

echo "$CALLER: exiting now with rc=1."

exit 1

}[/code]

調用腳本時,使用"-h"标志可以調用 "usage" 語句,如下所示:

./test-bucket-1 -h

8. 嘗試使用"安靜"的運作模式

您或許想讓腳本有兩種運作模式:

在 "verbose" 模式(您也許想将此作為預設值)中提示使用者輸入值,或者隻需按下 Enter 繼續運作。

在 "silent" 模式中将不提示使用者輸入資料。

下列摘錄說明了在安靜模式下運用所調用标志 "-s" 來運作腳本:

[code]# -------------------------------------------------

# Everything seems OK, prompt for confirmation

# -------------------------------------------------

if [ "$SILENT" = "yes" ]

then

RESPONSE="y"

else

echo "The $CALLER will be performed."

echo "Do you wish to proceed [y or n]? "

read RESPONSE # Wait for response

[ -z "$RESPONSE" ] && RESPONSE="n"

fi

case "$RESPONSE" in

[yY]|[yY][eE]|[yY][eE][sS])

;;

*)

echo "$CALLER terminated with rc=1."

exit 1

;;

esac[/code]

9. 當出現錯誤時,提供一個函數終止腳本

遇到嚴重錯誤時,提供一個中心函數以終止運作的腳本不失為一個好主意。此函數還可提供附加的說明,用于指導在此情況下應做些什麼:

[code]# ----------------------------------

# Subroutine to terminate abnormally

# ----------------------------------

terminate()

{

echo "The execution of $CALLER was not successful."

echo "$CALLER terminated, exiting now with rc=1."

dateTest=`date`

echo "End of testing at: $dateTest"

echo ""

exit 1

}[code]

10. 如有可能,提供可以執行簡單任務的函數

例如,不使用許多很長的行指令,如:

[code]# --------------------------------------------------

echo ""

echo "Creating Access lists..."

# --------------------------------------------------

Access -create -component Development -login ted -authority plead -verbose

if [ $? -ne 0 ]

then

echo "ERROR found in Access -create -component Development -login ted

-authority plead"

let "errorCounter = errorCounter + 1"

fi

Access -create -component Development -login pat -authority general -verbose

if [ $? -ne 0 ]

then

echo "ERROR found in Access -create -component Development -login pat

-authority general"

let "errorCounter = errorCounter + 1"

fi

Access -create -component Development -login jim -authority general -verbose

if [ $? -ne 0 ]

then

echo "ERROR found in Access -create -component Development -login jim

-authority general"

let "errorCounter = errorCounter + 1"

fi[/code]

……而是建立一個如下所示的函數,此函數也可以處理傳回碼,如果有必要,還可以增加錯誤計數器:

[code]CreateAccess()

{

Access -create -component $1 -login $2 -authority $3 -verbose

if [ $? -ne 0 ]

then

echo "ERROR found in Access -create -component $1 -login $2 -authority $3"

let "errorCounter = errorCounter + 1"

fi

}[/code]

……然後,以易讀和易擴充的方式調用此函數:

[code]# -------------------------------------------

echo ""

echo "Creating Access lists..."

# -------------------------------------------

CreateAccess Development ted projectlead

CreateAccess Development pat general

CreateAccess Development jim general[/code]

11. 當顯示正在生成的輸出時,捕獲每個腳本的輸出

如果腳本不能自動地将輸出發送到檔案的話,可以利用 Bash shell 的一些函數來捕獲所執行腳本的輸出,如:

./test-bucket-1 -s 2>&1 | tee test-bucket-1.out

讓我們來分析上面的指令:

"2>&1" 指令:

使 用 "2>&1" 将标準錯誤重定向到标準輸出。字元串 "2>&1" 表明任何錯誤都應送到标準輸出,即 UNIX/Linux 下 2 的檔案辨別代表标準錯誤,而 1 的檔案辨別代表标準輸出。如果不用此字元串,那麼所捕捉到的僅僅是正确的資訊,錯誤資訊會被忽略。

管道 "|" 和 "tee" 指令:

UNIX/Linux 程序和簡單的管道概念很相似。既然這樣,可以做一個管道将期望腳本的輸出作為管道的輸入。下一個要決定的是如何處理管道所輸出的内容。在這種情況下,我們會将它捕獲到輸出檔案中,在此示例中将之稱為 "test-bucket-1.out"。

但是,除了要捕獲到輸出結果外,我們還想監視腳本運作時産生的輸出。為達到此目的,我們連接配接允許兩件事同時進行的 "tee" (T- 形管道):将輸出結果放在檔案中同時将輸出結果顯示在螢幕上。 其管道類似于:

[code]process --> T ---> output file

|

V

screen[/code]

如果隻想捕獲輸出結果而不想在螢幕上看到輸出結果,那可以忽略多餘的管道: ./test-bucket-1 -s 2>&1 > test-bucket-1.out

假若這樣,相類似的管道如下:

process --> output file

12. 在每個腳本内,捕獲每個行指令所傳回碼

決定功能測試成功還是失敗的一種方法是計算已失敗行指令的數量,即傳回碼不是 0。變量 "$?" 提供最近所調用指令的傳回碼;在下面的示例中,它提供了執行 "ls" 指令的傳回碼。

[code]# -------------------------------------------

# The commands are called in a subroutine

# so that return code can be

# checked for possible errors.

# -------------------------------------------

ListFile()

{

echo "ls -al $1"

ls -al $1

if [ $? -ne 0 ]

then

echo "ERROR found in: ls -al $1"

let "errorCounter = errorCounter + 1"

fi

}[/code]

13. 記錄失敗事務的次數

在 功能測試中決定其成功或失敗的一個方法是計算傳回值不是 0 的行指令數量。但是,從我個人的經驗而言,我習慣于在我的 Bash shell 腳本中僅使用字元串而不是整數。在我所參考的手冊中沒有清楚地說明如何使用整數,這就是我為什麼想在此就關于如何使用整數和計算錯誤(行指令失敗)數量的 方面多展開講的原因:

首先,需要按如下方式對計數器變量進行初始化:

let "errorCounter = 0"

然後,發出行指令并使用 $? 變量捕獲傳回碼。如果傳回碼不是 0,那麼計數器增加 1(見藍色粗體語句):

[code]ListFile()

{

echo "ls -al $1"

ls -al $1

if [ $? -ne 0 ]

then

echo "ERROR found in: ls -al $1"

let "errorCounter = errorCounter + 1"

fi

}[/code]

順便說一下,與其它變量一樣,可以使用 "echo" 顯示整數變量。

14. 在輸出檔案中,為了容易辨別,突出顯示錯誤消息

當 遇到錯誤(或失敗的事務)時,除了錯誤計數器的數量會增加外,最好辨別出此處有錯。較理想的做法是,字元串有一個如 ERROR 或與之相似的子串(見藍色粗體的語句),這個子串允許測試者很快地在輸出檔案中查找到錯誤。此輸出檔案可能很大,而且它對于迅速找到錯誤非常重要。

[code]ListFile()

{

echo "ls -al $1"

ls -al $1

if [ $? -ne 0 ]

then

echo "ERROR found in: ls -al $1"

let "errorCounter = errorCounter + 1"

fi

}[/code]

15. 如有可能,"實時"生成檔案

在某些情況下,有必要處理應用程式使用的檔案。可以使用現有檔案,也可以在腳本中添加語句來建立檔案。如果要使用的檔案很長,那最好将其作為獨立的實體。如果檔案很小而且内容簡單或不相關(重要的一點是文本檔案而不考慮它的内容),那就可以決定"實時"建立這些臨時檔案。

下面幾行代碼顯示如何"實時"建立臨時檔案:

[code]cd $HOME/fvt

echo "Creating file softtar.c"

echo "Subject: This is softtar.c" > softtar.c

echo "This is line 2 of the file" >> softtar.c[/code]

第一個 echo 語句使用單個的 > 強行建立新檔案。第二個 echo 語句使用兩個 >> 将資料附加到現有檔案的後面。順便說一下,如果該檔案不存在,那麼會建立一個檔案。

16. 在執行腳本的過程中提供回報

最好在腳本中包含 echo 語句以表明它執行的邏輯進展狀況。可以添加一些能迅速表明輸出目的的語句。

如果腳本要花費一些時間執行,那或許應在執行腳本的開始和結束的地方列印時間。這樣可以計算出所花費的時間。

在腳本樣本中,一些提供進展說明的 echo 語句如下所示:

[code]# --------------------------------------------

echo "Subject: Product X, FVT testing"

dateTest=`date`

echo "Begin testing at: $dateTest"

echo ""

echo "Testcase: $CALLER"

echo ""

# --------------------------------------------

# --------------------------------------------

echo ""

echo "Listing files..."

# --------------------------------------------

# The following file should be listed:

ListFile $HOME/.profile

...

# --------------------------------------------

echo ""

echo "Creating file 1"

# --------------------------------------------[/code]

17. 提供腳本執行的摘要

如果正在計算錯誤或失敗事務的次數,那最好表明是否有錯誤。此方法使得測試者在看到輸出檔案的最後能迅速地辨認出是否存在錯誤。

在下面的腳本示例中,代碼語句提供了上述腳本的執行摘要:

[code]# --------------

# Exit

# --------------

if [ $errorCounter -ne 0 ]

then

echo ""

echo "*** $errorCounter ERRORS found during ***"

echo "*** the execution of this test case. ***"

terminate

else

echo ""

echo "*** Yeah! No errors were found during ***"

echo "*** the execution of this test case. Yeah! ***"

fi

echo ""

echo "$CALLER complete."

echo ""

dateTest=`date`

echo "End of testing at: $dateTest"

echo ""

exit 0

# end of file[/code]

18. 提供一個容易解釋的輸出檔案

在 腳本生成的實際輸出中提供一些關鍵資訊是非常有用的。那樣,測試者就可以很容易地确定正在檢視的檔案是否與自己所做的相關以及它是否是目前産生的。附加的 時間戳記對于是否是目前狀态是很重要的。摘要報告對于确定是否有錯誤也是很有幫助的;如果有錯誤,那麼測試者就必須搜尋指定的關鍵字,例如 ERROR,并确認出個别失敗的事務。

以下是一段輸出檔案樣本的片段:

[code]Subject: CMVC 2.3.1, FVT testing, Common, Part 1

Begin testing at: Tue Apr 18 12:50:55 EDT 2000

Database: DB2

Family: cmpc3db2

Testcase: fvt-common-1

Creating Users...

User pat was created successfully.

...

Well done! No errors were found during the

execution of this test case

fvt-common-1 complete.

End of testing at: Tue Apr 18 12:56:33 EDT 2000[/code]

當遇到錯誤時輸出檔案最後部分的示例如下所示:

[code]ERROR found in Report -view DefectView

*** 1 ERRORS found during the execution of this test case. ***

The populate action for the CMVC family was not successful.

Recreating the family may be necessary before

running fvt-client-3 again, that is, you must use 'rmdb',

'rmfamily', 'mkfamily' and 'mkdb -d',

then issue: fvt-common-1 and optionally, fvt-server-2.

fvt-client-3 terminated, exiting now with rc=1.

End of testing at: Wed Jan 24 17:06:06 EST 2001[/code]

19. 如有可能,提供清除腳本及傳回基準線的方法

測試腳本可以生成臨時檔案;假若這樣,最好能讓腳本删除所有臨時檔案。這就會避免由于測試者也許沒有删除所有臨時檔案而引起的錯誤,更糟糕的是将所需要的檔案當作臨時檔案而删除了。

運作功能測試的 Bash shell 腳本

本節描述如何運用 Bash shell 腳本進行功能測試。假設您已經執行了在前面部分中所述步驟。

設定必要的環境變量

根據需要在 .profile 中或手工指定下列環境變量。該變量用于說明在腳本中如何處理,所需環境變量的驗證必須在腳本執行前定義。

export TEST_VAR=1

将 Bash shell 腳本複制到正确的目錄下

Bash shell 腳本和相關檔案需要複制到要進行功能測試的使用者辨別的目錄結構下。

登入進某個帳戶。您應該在主目錄下。假設它是 /home/tester。

為測試案例建立目錄:mkdir fvt

複制 Bash shell 腳本和相關檔案。擷取壓縮檔案(請參閱參考資料)并将其放在 $HOME 下。然後将其按下列方式解壓:unzip trfvtbash.zip

為了執行這個檔案,更改檔案的許可權:chmod u+x *

更改名稱以除去檔案的字尾:mv test-bucket-1.bash test-bucket-1

運作腳本

執行下列步驟以運作腳本:

以測試者的使用者辨別登入

更改目錄至所複制腳本的位置:cd $HOME/fvt

從 $HOME/fvt 運作腳本:./test-bucket-1 -s 2>&1 | tee test-bucket-1.out

看一下輸出檔案 "test-bucket-1.out" 的尾部并檢視摘要報告的結論。