您的位置:軟件測試 > 軟件項目管理 > 項目案例分析 >
測試為先/測試驅動案例分析
作者:網(wǎng)絡轉載 發(fā)布時間:[ 2013/9/29 15:45:10 ] 推薦標簽:

  進行測試為先測試驅動的程序設計是確保敏捷開發(fā)順進行的有效措施。這篇案例將為讀者提供詳細的開發(fā)歷程,來分析測試為先測試驅動的程序設計的過程。本文的重點:

簡要重復敘述一下測試為先/測試驅動得好處。
簡要介紹一下案例中的項目。
沒有利用測試為先/測試驅動設計的單元代碼是什么樣的?
沒有利用測試為先/測試驅動設計的單元代碼里有什么樣的問題?
針對單元代碼設計的手動單元測試。
查找出毛病后的改進代碼。

測試為先/測試驅動得好處

  傳統(tǒng)的瀑布型軟件開發(fā)是先從客戶那里獲得需求,然后進行紙上談兵的設計,接著是程序源碼寫作構建,后才是測試者對質量進行檢評。從需求的分析到后的測試,兩者的相隔往往有好幾個月。等到測試發(fā)現(xiàn)結構性問題時,重新設計已經(jīng)成為一個無法完成的任務。設計者程序員已經(jīng)無法回到幾個月前推翻紙上談兵的錯誤設計,重新用新的方式進行代碼編寫組合。測試為先/測試驅動和瀑布型軟件開發(fā)不同是:


測試模擬用戶的使用組件的應用方式,為開發(fā)者提供解決方案;
測試驅使開發(fā)者開發(fā)可以測試的部件;
及早測試,盡快排除設計中的各種微小問題;
測試為開發(fā)者提供質保底線,每次的部件更改都能利用測試來檢測修改后質量。

  以上這些都是我以前重復過的。這些說的容易但是想像起來是比較難一點。我下面所要談到的案例并不是教科書里的完美案例,所謂的完美案例是完全可以自動化,完全可以進行單元測試的程序組件。舉個例子說,想像你要設計一個類來代表“復數(shù)”(imaginary number),這樣一個類是可以完全進行自動化單元測試。這種情況只能算得上百分之五十的現(xiàn)實情況,在其他百分之五十的狀況下,一些手動測試和一些自動化測試都是必要的。還有很多情況下,手動測試是的選擇。半自動和手動測試并不代表整個開發(fā)不算作測試驅動開發(fā)。測試驅動的多數(shù)人都會說手動測試和半自動化測試并不能代表團隊在進行測試驅動的開法。我覺得這種說法是偏見,只要測試組和開發(fā)組能夠配合,盡可能地在早時間將用戶需求確定后,讓測試組開始針對用戶需求,設計思路進行測試用例設計,開發(fā)和測試能同時進行,開發(fā)出的部件能夠迅速進行測試,測試用例能夠經(jīng)常地運行確保開發(fā)的質量不受變化的影響。這是測試為先/測試驅動的開發(fā)。


本文的案例簡介

  用來演示測試為先/測試驅動的開發(fā),我將使用我近設計的一個將應用程序圖標加入System Tray里的類。然后在應用程序退出后,自動將圖標從System Tray里刪除。這樣的類,你如果知道Windows系統(tǒng)對System Tray里的圖標管理,知道設計這么一個類的自動化測試并不簡單。我覺得這種和圖形界面打交道的類,也沒有必要地進行自動化測試。所以我對這個類的測試驅動采取手工測試為主的測試,以測試者甚至開法者本身用用戶的需求,先用例程作為基礎,來設計圖標管理類的單元測試。

案例的用戶需求

  我是這個類的用戶,對于我要設計的程序,我的使用是很簡單的。下面的列表是我的需求:

System Tray里的應用圖標的數(shù)據(jù)管理和圖標的加入刪除都由類對象來進行;
類對象能夠設定視窗柄;
類對象能夠設定圖標的獨特ID;
類對象能夠設定圖標對系統(tǒng)信息處理的消息ID;
類對象能夠設定圖標在鼠標指向后能夠顯示有關程序的信息(程序的名稱,設計公司和其他信息);

類對象必須在調(diào)用者的指示下將圖標放入System Tray。
類對象必須處理讓調(diào)試者能夠刪除System Tray里的圖標。
類對象在自我摧毀的時候自動刪除System Tray里的圖標。

  這些用戶需求是我要設計使用案例,在敏捷中,這些案例是一個個故事。我在設計每一個故事的編碼之前先設計一個測試案例。每個測試案例都在設計完成之前會運行失敗。設計完成后,這些測試案例才能順利運行。

  為了調(diào)試圖標的加入和刪除都能正確運行,我決定使用一個簡單的Win32視窗程序來作為我的單元測試溫床,我的測試是手動測試。我的目標是用單元測試來盡可能地覆蓋我設計的代碼面積。第一步我設計了以下的單元測試:

void UnitTestCase0(HWND hWnd, HICON handleIcon)
{
// a normal core functionality test.
gSysTrayIcon.SetTrayIconID(11200);
gSysTrayIcon.SetNotifyWindow(hWnd);
gSysTrayIcon.SetTrayIcon(handleIcon);

gSysTrayIcon.SetTrayIconTip(_T("SysTrayIcon"));
gSysTrayIcon.SetTrayIconWmMsg(WM_TRAYICON_MSGS);
if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}

  這是一個很簡單的函數(shù),我假設我有一個全局變量叫gSysTrayIcon。它是一個類對象;它有至少六個函數(shù);它的五個函數(shù)是數(shù)據(jù)設定函數(shù);它的后一個函數(shù)是讓調(diào)用者告訴它把圖像加入System Tray。根據(jù)我自己設計的單元測試案例,我設計了以下的類:

#ifndef SYS_TRAY_ICON_H_
#define SYS_TRAY_ICON_H_

#include "shellapi.h"

class SysTrayIcon
{
private:
NOTIFYICONDATA niData;

public:
SysTrayIcon();
~SysTrayIcon();

void SetTrayIconID(UINT iconID);
void SetNotifyWindow(HWND hWnd);
void SetTrayIcon(HICON iconHandle);
void SetTrayIconTip(LPCTSTR szMsg);
void SetTrayIconWmMsg(UINT wmMsg);

BOOL AddIconToSysTray();
BOOL DeleteIconFromSysTray();

};

#endif

  我的類成員設計如下,這里面有很多我無意中犯下的錯誤,也有我故意設置的錯誤,后面我用單元測試一點點地查找出一些常見的問題。為了順利通過我上面的單元測試,首先看看我的設計初稿: #include "StdAfx.h"

#include "SysTrayIcon.h"
#include <string.h>


SysTrayIcon::SysTrayIcon()
{
ZeroMemory(&niData, sizeof(NOTIFYICONDATA));
niData.cbSize = (DWORD)sizeof(NOTIFYICONDATA);
niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP;
}

SysTrayIcon::~SysTrayIcon()
{
DeleteIconFromSysTray();
}

void SysTrayIcon::SetTrayIconID(UINT iconID)
{
niData.uID = iconID;
}

void SysTrayIcon::SetNotifyWindow(HWND hWnd)
{
niData.hWnd = hWnd;
}

void SysTrayIcon::SetTrayIcon(HICON iconHandle)
{
niData.hIcon = iconHandle;
}

void SysTrayIcon::SetTrayIconWmMsg(UINT wmMsg)
{
niData.uCallbackMessage = wmMsg;
}

void SysTrayIcon::SetTrayIconTip(LPCTSTR szMsg)
{
_tcscpy(niData.szTip, szMsg);
}

BOOL SysTrayIcon::AddIconToSysTray()
{
Shell_NotifyIcon(NIM_ADD, &niData);
return TRUE;
}

BOOL SysTrayIcon::DeleteIconFromSysTray()
{
return Shell_NotifyIcon(NIM_DELETE, &niData);
}

  敏捷的宗旨是,在短的時間內(nèi)為客戶提供完整的設計,讓客戶能夠看到期待的價值,讓客戶能迅速反饋,并把反饋意見轉變?yōu)樵O計改進。我以上的代碼給我自己提供一個可以測試的機會。我用我的測試案例來實踐我的設計,測試程序是一個SDI視窗程序。程序運行開始先把一個圖標放入System Tray,然后,用戶可以按在程序的縮小按鈕上,程序會消失,但是System Tray里的程序圖標。用戶用鼠標左鍵雙擊System Tray里的程序圖標,程序視窗會重新出現(xiàn)在桌面上。用戶把鼠標光標移到System Tray里的程序圖標上,一秒鐘后會一個提示標題出現(xiàn),顯示程序的名稱。當我關閉程序視窗,視窗消失,System Tray里的程序圖標也一并消失。這是我的第一個測試。這個測試案例運行,不會出現(xiàn)任何問題。

  我寫的第一個案例是開發(fā)者通常會做的測試,一個簡單的案例保證設計到達基本的用戶需求。作為認真的開發(fā)者,和有專業(yè)意識的QA,這樣簡單的測試根本不夠。各種各樣的邊界問題會通過設計的空隙造成程序運行異常。我設計了另一個測試邊際問題的測試,代碼如下:

void UnitTestCase1(HWND hWnd, HICON handleIcon)

{
gSysTrayIcon.AddIconToSysTray();
}

 

  這個案例其實很簡單。假設我建立了一個gSysTrayIcon,但是我不對其做任何初始化設定。那會出現(xiàn)什么問題?我運行一下這個案例,結果我馬上發(fā)現(xiàn)了兩個問題,一是ystem Tray里的程序圖標是一個空格。接著我把標光標移到System Tray里的程序圖標的位置上,馬上那個位置被其他圖標給占據(jù)了。這些行為都是不對的。

  仔細看看我的設計,我在調(diào)用AddIconToSysTray()之前,沒有調(diào)用一些重要的對象處理,這三個:SetNotifyWindow(HWND hWnd),SetTrayIcon(HICON iconHandle),和SetTrayIconWmMsg(UINT wmMsg)。所以我的案例會出現(xiàn)異常。在現(xiàn)實中,測試或者開發(fā)者自己都能運用自己的經(jīng)驗和知識來判斷這些邊界的問題,然后用單元測試來鑒別設計在處理這些問題的能力,F(xiàn)在我已經(jīng)了解到我的設計有毛病,要想辦法解決。首先看看SetNotifyWindow(HWND hWnd),這個函數(shù)的邊界是HWND參數(shù)不能是NULL(或是0)。如果這種情況出現(xiàn),我應該如何處理?我的解決是用扔出異常。我要為以上的單元測試進行一點改變。下面是我的修改:

void UnitTestCase1(HWND hWnd, HICON handleIcon)

{
// without any initailization.
try
{
if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}
catch(const AppException& e)
{
::MessageBox(hWnd, e.ToString(), _T("Error:"), MB_OK);
}
}

  然后我再更改我的設計,促使我的設計在處理錯誤輸入時會拋出異常:

void SysTrayIcon::SetNotifyWindow(HWND hWnd)
{
if (hWnd == NULL)
{
// throw excepttion
throw AppException(_T("The handle of the window is invalid."));
}
niData.hWnd = hWnd;
}

  我再運行一下我的案例,結果還是不行,原來的毛病一點都沒有改變。我再看看我的測試案例,結果發(fā)現(xiàn)我的修改并沒有解除我所面對的問題。在我調(diào)用AddIconToSysTray()之前,我根本沒有調(diào)用SetNotifyWindow,所以我的測試案例根本沒有解決我的問題。我要修改的是AddIconToSysTray()。下面是我的修改:

BOOL SysTrayIcon::AddIconToSysTray()
{
if (niData.hWnd == NULL)
{
throw AppException(_T("The handle of the window is invalid."));

}
else if (niData.hIcon == NULL)
{
throw AppException(_T("The handle of the icon is invalid."));
}
else if (niData.uCallbackMessage == 0)
{
throw AppException(_T("The callback message ID is invalid."));
}


BOOL retVal = Shell_NotifyIcon(NIM_ADD, &niData);
return retVal;
}

  修改后運行一下,我的程序輸出了異常信息提示,當我選擇提示的“OK”按鈕后,程序沒有在System Tray里添加程序圖標。我為了測試剩下兩個判斷分支,設計了兩個案例里,加上上一個案例我有三個:

void UnitTestCase1(HWND hWnd, HICON handleIcon)
{
// without any initailization.
try
{
if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}
catch(const AppException& e)
{
::MessageBox(hWnd, e.ToString(), _T("Error:"), MB_OK);
}
}

void UnitTestCase2(HWND hWnd, HICON handleIcon)
{
// without any initailization on ICON handle.
try
{
gSysTrayIcon.SetNotifyWindow(hWnd);

if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}
catch(const AppException& e)
{
::MessageBox(hWnd, e.ToString(), _T("Error:"), MB_OK);
}
}

void UnitTestCase3(HWND hWnd, HICON handleIcon)
{
// without any initailization for message callback ID.
try
{
gSysTrayIcon.SetNotifyWindow(hWnd);
gSysTrayIcon.SetTrayIcon(handleIcon);
if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}
catch(const AppException& e)
{
::MessageBox(hWnd, e.ToString(), _T("Error:"), MB_OK);
}
}

  還有什么可以測試?首先,NOTIFYICONDATA::uID的值有沒有限度?我們可以試試0和-1(-1應該是32位正值整數(shù)的大值),對程序的影響也不大:


void UnitTestCase4(HWND hWnd, HICON handleIcon)
{
// What happen to have Icon ID to be 0.
gSysTrayIcon.SetTrayIconID(0);

gSysTrayIcon.SetNotifyWindow(hWnd);
gSysTrayIcon.SetTrayIcon(handleIcon);
gSysTrayIcon.SetTrayIconTip(_T("SysTrayIcon"));
gSysTrayIcon.SetTrayIconWmMsg(WM_TRAYICON_MSGS);
if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}

void UnitTestCase5(HWND hWnd, HICON handleIcon)
{
// What happen to have Icon ID to be 0.
gSysTrayIcon.SetTrayIconID(-1);
gSysTrayIcon.SetNotifyWindow(hWnd);
gSysTrayIcon.SetTrayIcon(handleIcon);
gSysTrayIcon.SetTrayIconTip(_T("SysTrayIcon"));
gSysTrayIcon.SetTrayIconWmMsg(WM_TRAYICON_MSGS);
if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}

  后是鼠標滑到System Tray的圖標時要顯示的字符串。NOTIFYICONDATA::szTip大容量應該是64個字節(jié)。我用以下的測試案例來測試我的設計:

void UnitTestCase6(HWND hWnd, HICON handleIcon)
{
// What happen to have Icon ID to be 0.
gSysTrayIcon.SetTrayIconID(11200);
gSysTrayIcon.SetNotifyWindow(hWnd);
gSysTrayIcon.SetTrayIcon(handleIcon);
gSysTrayIcon.SetTrayIconTip(_T("kdhfhdfjhdsfhdsjfhdsjhfjdshfjdshfjdshjfhdsjfsjdhfjdshjs"
"hdfhdsfjhdsjfhsdjfhdshfhdsjfhdsfhsdjhfsdjhfjdshfjhdsfjhdsfhsdhfsjdhfjshdjfhdsfjhsdj"
"dfhhdsjfhdjshfjdhfjdhfdhfjhdsfjhdjhfjdsfhjsadhhdskfhadskfhdskjfhkdsjfhkdsjhfkjadshf"
"kjasdhkfhadskfhdskjhfkadsjhfkfashkfhaskdhfkadsfhkdsafhkdsjhfkdsahfkdshkjfhdaskhkads"
"hfkdshfkjdhfkjdhfkdshfiohirhu'prhpiurhf"));
gSysTrayIcon.SetTrayIconWmMsg(WM_TRAYICON_MSGS);
if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}

  讓我吃驚的是,程序并沒有出現(xiàn)任何異常。只是64字節(jié)以后的字節(jié)都被忽略了。這種現(xiàn)象并不代表我的設計沒有問題,我的原有設計是這樣的:

void SysTrayIcon::SetTrayIconTip(LPCTSTR szMsg)
{
_tcscpy(niData.szTip, szMsg);
}

  _tcscpy是個很不安全的函數(shù),它不檢測接受緩沖的容量,所以,原緩沖的容量可以比接受緩沖的容量大,這樣的代碼很容易出現(xiàn)buffer overflow。所以,我們必須在字符串拷貝的時候檢測兩個緩沖的容量大小,接受緩沖的容量必須比原緩沖的容量要大。但是在我們現(xiàn)在所面對的情況下,這種情況我們采用另一種解決方式比較容易,是保證字符串的拷貝不超過一定的數(shù)量。而且我們采用一個比較安全的拷貝函數(shù)。首先我改變了原有的測試用例:


void UnitTestCase6(HWND hWnd, HICON handleIcon)
{
// What happen to have Icon ID to be 0.
gSysTrayIcon.SetTrayIconID(11200);
gSysTrayIcon.SetNotifyWindow(hWnd);
gSysTrayIcon.SetTrayIcon(handleIcon);
try
{
gSysTrayIcon.SetTrayIconTip(_T("kdhfhdfjhdsfhdsjfhdsjhfjdshfjdshfjdshjfhdsjfsjdhfjdshjs"
"hdfhdsfjhdsjfhsdjfhdshfhdsjfhdsfhsdjhfsdjhfjdshfjhdsfjhdsfhsdhfsjdhfjshdjfhdsfjhsdj"
"dfhhdsjfhdjshfjdhfjdhfdhfjhdsfjhdjhfjdsfhjsadhhdskfhadskfhdskjfhkdsjfhkdsjhfkjadshf"
"kjasdhkfhadskfhdskjhfkadsjhfkfashkfhaskdhfkadsfhkdsafhkdsjhfkdsahfkdshkjfhdaskhkads"
"hfkdshfkjdhfkjdhfkdshfiohirhu'prhpiurhf"));
}
catch(const AppException& e)
{
::MessageBox(hWnd, e.ToString(), _T("Error:"), MB_OK);
}
gSysTrayIcon.SetTrayIconWmMsg(WM_TRAYICON_MSGS);
if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}

  用我原來的設計來測試以上的測試用例,什么都不會發(fā)生。說明我的原有設計要改變一些:

void SysTrayIcon::SetTrayIconTip(LPCTSTR szMsg)
{
HRESULT hr = StringCchCopyN(niData.szTip, 63, szMsg, 63);
if (FAILED(hr))
{
// throw exception
throw AppException(_T("Invalid tip string"));
}
}

  使用更新的代碼,再運行我的測試用例,讓我意外的是,測試用例竟然報道程序出錯。說明我的更改是正確的。我的希望是如果原緩沖的容量太大,程序只拷貝接受緩沖容量所能承受的字符串量。這樣我的測試用例應該不會報錯。是什么造成這個問題?仔細查詢一下有關StringCchCopyN()的說明,發(fā)現(xiàn)我的問題在哪里了。如果原緩沖的容量太大,程序只拷貝接受緩沖容量所能承受的字符串量,StringCchCopyN()的返回值是STRSAFE_E_INSUFFICIENT_BUFFER,而不是S_OK。所以我的源代碼必須進行一定的變化:

void SysTrayIcon::SetTrayIconTip(LPCTSTR szMsg)
{
HRESULT hr = StringCchCopyN(niData.szTip, 63, szMsg, 63);
if (FAILED(hr))
{
if (hr != STRSAFE_E_INSUFFICIENT_BUFFER)
{
// throw exception
throw AppException(_T("Invalid tip string"));
}
}
}

  再次運行上面的測試用例,我不再看見原有的錯誤消息。你可以看出我到現(xiàn)在,一直在使用我的測試和我對我要設計的代碼的結構的熟悉來指導我的設計。白盒測試不僅能提供我所需要的程序質量檢測,同時也指引我的設計方向。再試試幾個其他的案例,這是一個典型的案例,如果我用NULL作為原緩沖,那會出現(xiàn)什么問題:

void UnitTestCase8(HWND hWnd, HICON handleIcon)
{
// What happen to have Icon ID to be 0.
gSysTrayIcon.SetTrayIconID(11200);
gSysTrayIcon.SetNotifyWindow(hWnd);
gSysTrayIcon.SetTrayIcon(handleIcon);
gSysTrayIcon.SetTrayIconTip(NULL);
gSysTrayIcon.SetTrayIconWmMsg(WM_TRAYICON_MSGS);
if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}

  試試之后的結果是整個測試程序垮掉。我們又發(fā)現(xiàn)了一個問題。我先更改一下我的測試用例:

void UnitTestCase8(HWND hWnd, HICON handleIcon)
{
// What happen to have Icon ID to be 0.
gSysTrayIcon.SetTrayIconID(11200);
gSysTrayIcon.SetNotifyWindow(hWnd);
gSysTrayIcon.SetTrayIcon(handleIcon);
try
{
gSysTrayIcon.SetTrayIconTip(NULL);
}
catch(const AppException& e)
{
::MessageBox(hWnd, e.ToString(), _T("Error:"), MB_OK);
}
gSysTrayIcon.SetTrayIconWmMsg(WM_TRAYICON_MSGS);
if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}

  然后再修改我的設計:

void SysTrayIcon::SetTrayIconTip(LPCTSTR szMsg)
{
if (szMsg == NULL)
{
throw AppException(_T("Tip string pointer cannot be NULL."));
}

HRESULT hr = StringCchCopyN(niData.szTip, 63, szMsg, 63);
if (FAILED(hr))
{
if (hr != STRSAFE_E_INSUFFICIENT_BUFFER)
{
// throw exception
throw AppException(_T("Invalid tip string"));
}
}
}

  運行后看到我所希望看到的錯誤信息。這樣我又防止了一個毛病漏到測試組的手上。

  后在總結之前,說說圖標柄輸入如果是NULL的情況,我們可以進行一些改進,如果用戶在調(diào)用SysTrayIcon::SetTrayIcon(HICON iconHandle)輸入非法值NULL,解決方法不一定是要直接拋出異常。我們可以讓系統(tǒng)幫助設定一個默認圖標柄。首先我們再Copy & paste制作一個新的單元測試:

void UnitTestCase9(HWND hWnd, HICON handleIcon)
{
// What happen to have Icon ID to be 0.
gSysTrayIcon.SetTrayIconID(15923);

gSysTrayIcon.SetNotifyWindow(hWnd);
gSysTrayIcon.SetTrayIcon(NULL);
gSysTrayIcon.SetTrayIconTip(_T("SysTrayIcon"));
gSysTrayIcon.SetTrayIconWmMsg(WM_TRAYICON_MSGS);
if (!gSysTrayIcon.AddIconToSysTray())
{
::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
return;
}
}

  運行以上的單元測試,我馬上看見原有的異常被拋出,表明我的測試失敗了。我的意圖是如果圖標柄的設置是NULL,那么,我讓系統(tǒng)找到一個默認的圖標,并用它作為程序在SystemTray里的圖標。我對代碼進行以下修改:

BOOL SysTrayIcon::AddIconToSysTray()
{
if (niData.hWnd == NULL)
{
throw AppException(_T("The handle of the window is invalid."));
}
else if (niData.hIcon == NULL)
{
HICON defaultIcoHdl = ::LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
if (defaultIcoHdl != NULL)
{
niData.hIcon = defaultIcoHdl;
}
else
{
throw AppException(_T("The handle of the icon is invalid."));
}
}
else if (niData.uCallbackMessage == 0)
{
throw AppException(_T("The callback message ID is invalid."));
}

BOOL retVal = Shell_NotifyIcon(NIM_ADD, &niData);
return retVal;
}

  運行我的單元測試,結果沒有出錯,但是我的第二個單元測試原來設計為,如果圖標柄的設置是NULL,拋出異常,現(xiàn)在這個使用案例已經(jīng)沒有意義了,所以第二個單元測試可以被刪掉了。

總結

  我的這篇文章完整(并非完美)地展示了一個簡單的測試為先,測試驅動的開發(fā)案例。設計過程中,我用測試案例來主導我的設計,只有簡單的設計來實現(xiàn)我的需求。我只在更改錯誤的情況下增加功能,而不是隨便憑著自己的想像來增加我不需要的功能。我在測試中找出了不少我問題,而且都是在開發(fā)過程中發(fā)現(xiàn)的問題,也是說在開法的基本階段測試開始進行了,而且很多問題在開發(fā)初期被檢測出來并修改好,盡早測試,可以為后面的開發(fā)減少很多不必要的困難。

  我這個案例不是一個完美的案例,F(xiàn)實中,能夠完美展示單元測試的好處的完美案例是不存在的,所有我見過的完美案例都是在教科書里出現(xiàn)的。這些案例有時給人的感覺是不真實,也展示出這些案例的局限性。我的案例在很大程度上依賴手動化測試,這有時是違反敏捷開發(fā)的用意的。在敏捷開發(fā)中,自動化單元測試和接受性測試是非常重要的。我敢說很多進行敏捷開發(fā)的專家都會說我的案例算不上敏捷開發(fā)。我對這一觀點只同意到一定的程度,軟件開發(fā)是個人與人互動的社會活動,開發(fā)者在手動測試上所花時間過多的話,要將這樣的任務推給QA,有能力的QA應該可以和開發(fā)者一起考慮什么樣的接受性測試和單元測試能夠幫助整個團隊提交更好的產(chǎn)品。

  我這個案例同時也說明,用戶界面的設計也能通過單元測試來進行。這樣的測試不僅僅是開發(fā)者自己進行,有能力的QA可以和開發(fā)者一起進行接受性測試,QA可以享受一下開發(fā)的樂趣。同時可以和開發(fā)者一起合作進行質量監(jiān)控,這樣雙方不會因為競爭而感受雙方的相互威脅,QA幫助開發(fā)者及早進行測試,從而建立友好的合作關系。

軟件測試工具 | 聯(lián)系我們 | 投訴建議 | 誠聘英才 | 申請使用列表 | 網(wǎng)站地圖
滬ICP備07036474 2003-2017 版權所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd