使用測試優(yōu)先方法開發(fā)用戶界面_第1頁
使用測試優(yōu)先方法開發(fā)用戶界面_第2頁
使用測試優(yōu)先方法開發(fā)用戶界面_第3頁
使用測試優(yōu)先方法開發(fā)用戶界面_第4頁
使用測試優(yōu)先方法開發(fā)用戶界面_第5頁
已閱讀5頁,還剩7頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、使用測試優(yōu)先方法開發(fā)用戶界面 本文作者 未知 摘自 機(jī)電之家 1、概述測試優(yōu)先是測試驅(qū)動開發(fā)(Test-Driven Development, TDD)的核心思想,它要求在編寫產(chǎn)品代碼前先編寫基于產(chǎn)品代碼的測試代碼。在測試驅(qū)動開發(fā)的單元測試中,對GUI應(yīng)用實(shí)施自動測試應(yīng)該是測試驅(qū)動開發(fā)的軟肋之一。由于界面的操作是有由人來完成的,所以要想在GUI中完成單元自動測試是有一定難度的。Kent Beck在它的測試驅(qū)動開發(fā)中就曾提到過這個問題。本文將通過一個例子來講解在測試驅(qū)動開發(fā)中如何針對GUI進(jìn)行單元測試。這個例子是 David Astels著的測試驅(qū)動開發(fā)實(shí)用指南(影印版)中一個關(guān)于影片列表管理的

2、例子。該書中文版即將在國內(nèi)出版。書中討論并介紹了開發(fā)這個例子的多種方法。筆者將介紹其中的一種,并且為了方便使用C 的朋友的學(xué)習(xí),書中的代碼我用C 寫了一遍,類名和變量名盡量和原書保持一致,以方便閱讀該書的C 讀者。在此也要感謝David Astels給我們帶來如此精彩的一本書。本文敘述背景為:CppUnit1.9.0, Visual C 6.0, Windows2000 pro。文中敘述有誤之處,敬請批評指正。如果讀者對CppUnit還沒有一定的了解,可以先參考筆者的另一篇文章CppUnit測試框架入門。2、需求分析對于這個影片管理的應(yīng)用,我們主要實(shí)現(xiàn)增加、刪除和顯示影片列表的功能?;谶@些需

3、求,我們可以畫一張GUI草圖。界面的控件主要有:一個顯示所有影片的列表listbox控件,一個填寫新的影片名的edit控件,一個增加button控件,一個刪除button控件。由此,我們的開發(fā)目標(biāo)就十分的明確了。3、編寫UI測試代碼這部分的UI測試代碼主要是測試各個控件是否正確生成并且是可見的,以及測試一些控件的label文字是否正確。我們從TestCase繼承一個類TestWidgets用于測試窗口,并添加四個測試,分別測試listbox、edit、add button、delete button。class TestWidgets : public CppUnit:TestCase CPP

4、UNIT_TEST_SUITE(TestWidgets); CPPUNIT_TEST(testList); CPPUNIT_TEST(testField); CPPUNIT_TEST(testAddButton); CPPUNIT_TEST(testDeleteButton); CPPUNIT_TEST_SUITE_END();public: TestWidgets(); virtual TestWidgets();public: virtual void setUp(); virtual void tearDown(); void testList(); void testField();

5、void testAddButton(); void testDeleteButton();private: MovieListWindow* m_pWindow;其中,MovieListWindow是一個窗口類。我們來看看其中的一個測試,請看代碼中的注釋。void TestWidgets:testAddButton() /得到btn指針 CButton* pAddButton = m_pWindow->GetAddButton(); /檢查是否生成btn CPPUNIT_ASSERT(pAddButton->m_hWnd); /檢查btn是否可見 CPPUNIT_ASSERT_E

6、QUAL(TRUE, :IsWindowVisible(pAddButton->m_hWnd); CString strText; pAddButton->GetWindowText(strText); CString strExpect = "Add" /檢查btn的Label文字是否正確 CPPUNIT_ASSERT_EQUAL(strExpect, strText);編譯測試代碼,編譯器會給我們一些出錯信息。這要求我們必須馬上編寫產(chǎn)品代碼以讓編譯通過。首先第一個要實(shí)現(xiàn)的產(chǎn)品代碼就是MovieListWindow窗口類。class AFX_EXT_CLASS

7、 MovieListWindow : public CDialogpublic: MovieListWindow(CWnd* pParent = NULL); / standard constructor CListBox* GetMovieListBox()return &m_MovieListBox; CEdit* GetMovieField()return &m_MovieField; CButton* GetAddButton()return &m_AddBtn; CButton* GetDeleteButton()return &m_DeleteBtn

8、; void Init(); / Dialog Data /AFX_DATA(MovieListWindow) enum IDD = IDD_MOVIELISTDLG ; CButton m_AddBtn; CButton m_DeleteBtn; CEdit m_MovieField; CListBox m_MovieListBox; /AFX_DATA / Overrides / ClassWizard generated virtual function overrides /AFX_VIRTUAL(MovieListWindow) protected: virtual void DoD

9、ataExchange(CDataExchange* pDX); / DDX/DDV support /AFX_VIRTUAL / Implementation protected: / Generated message map functions /AFX_MSG(MovieListWindow) /AFX_MSG DECLARE_MESSAGE_MAP();在MovieListWindow窗口類中我們實(shí)現(xiàn)了需要的控件以及針對這些控件的一些方法,如GetMovieListBox()等,本文在此不做詳述。編譯測試代碼和產(chǎn)品代碼,檢查是否通過。如未通過則繼續(xù)檢查產(chǎn)品代碼以使編譯和測試通過。 4

10、、編寫控件行為測試代碼 接下來應(yīng)該是編寫點(diǎn)擊add button和delete button的測試代碼了。同樣,我們從TestCase繼承出TestOperation: class TestOperation : public CppUnit:TestCase CPPUNIT_TEST_SUITE(TestOperation); CPPUNIT_TEST(testMovieList); CPPUNIT_TEST(testAdd); CPPUNIT_TEST(testDelete); CPPUNIT_TEST_SUITE_END();public: void testMovieList(); v

11、oid testAdd(); void testDelete();public: void setUp(); void tearDown(); TestOperation(); virtual TestOperation();private: static CString LOST_IN_SPACE; CStringArray m_MovieNames; MovieListWindow* m_pWindow; MovieListEditor* m_pEditor; 你會發(fā)現(xiàn),在TestOperation類中出現(xiàn)了一個成員變量MovieListEditor* m_pEditor。類MovieLi

12、stEditor是一個用來保存影片數(shù)據(jù)以及對影片數(shù)據(jù)進(jìn)行增加,刪除操作的管理類。后面我們會給出它的實(shí)現(xiàn)??纯磗etUp()做了什么: void TestOperation:setUp() /創(chuàng)建一個MovieListEditor實(shí)例 m_pEditor = new MovieListEditor(); m_MovieNames.RemoveAll(); /將MovieListEditor中的影片列表拷貝到m_MovieNames,為后面測試作準(zhǔn)備 for(int n=0; n<m_pEditor->GetMovies()->GetSize(); n ) m_MovieName

13、s.Add(m_pEditor->GetMovies()->GetAt(n); 我們來看看添加影片的測試,請看代碼注釋: void TestOperation:testAdd() /拷貝一份movie list CStringArray MovieNamesWithAddition; for(int n=0; n<m_MovieNames.GetSize(); n ) MovieNamesWithAddition.Add(m_MovieNames.GetAt(n); MovieNamesWithAddition.Add(LOST_IN_SPACE); /生成窗口 MovieL

14、istWindow *pWindow = new MovieListWindow(m_pEditor); pWindow->Init(); /填寫新的影片的名稱 CEdit* pEdit = pWindow->GetMovieField(); pEdit->SetWindowText(LOST_IN_SPACE); /點(diǎn)擊add btn CButton* pBtn = pWindow->GetAddButton(); :SendMessage(pBtn->m_hWnd, BM_CLICK, 0, 0); /檢查列表控件中是否已加入新的影片 CListBox* pL

15、istBox = pWindow->GetMovieListBox(); CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox->GetCount(); /檢查列表控件中影片名是否正確 CString strNewMovieName; pListBox->GetText(pListBox->GetCount()-1, strNewMovieName); CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, strNewMovieName); /銷毀窗口 pWindow->D

16、estroyWindow(); delete pWindow; pWindow = NULL; 編譯后會有出錯信息,主要的錯誤有:a)、我們把m_pEditor保存在MovieListWindow中了,這需要我們修改原來的MovieListWindow的構(gòu)造函數(shù)。 b)、沒有MovieListEditor類。MovieListEditor的實(shí)現(xiàn)如下: class AFX_EXT_CLASS MovieListEditor public: MovieListEditor(); virtual MovieListEditor();public: virtual CStringArray* GetM

17、ovies()return &m_arMovieList; virtual void Add(CString strMovie)m_arMovieList.Add(strMovie); virtual void Delete(int nIndex)m_arMovieList.RemoveAt(nIndex);private: CStringArray m_arMovieList; 再次編譯,已經(jīng)通過.運(yùn)行測試,發(fā)現(xiàn)在: CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox->GetCount(); 測試通不

18、過。檢查后知道原因是,我們在測試代碼里: :SendMessage(pBtn->m_hWnd, BM_CLICK, 0, 0); 給add button發(fā)送了點(diǎn)擊按鈕的消息,但是在MovieListWindow 窗口中我們沒有加入消息的響應(yīng)函數(shù),因此測試沒有通過。趕緊添加消息響應(yīng)函數(shù)。 void MovieListWindow:onClickAddButton() UpdateData(); CString strNewMovieName; m_MovieField.GetWindowText(strNewMovieName); if("" != strNewMovi

19、eName) m_pEditor->Add(strNewMovieName); m_MovieListBox.AddString(strNewMovieName); 編譯、測試、通過。 5、Mock Objects 在刪除操作的單元測試中,我們遇到的一個問題是,影片列表的數(shù)據(jù)應(yīng)該是保存在一個文本文件或者數(shù)據(jù)庫當(dāng)中的,如果我們編寫的測試依賴于這些實(shí)際的文件或數(shù)據(jù)庫,那么我們的測試就會受制于這些外部的資源。一旦文件或者數(shù)據(jù)庫里的數(shù)據(jù)發(fā)生變化,必然會波及到我們的測試代碼,從而產(chǎn)生錯誤的測試信息。前面的MovieListEditor中我們沒有加入一些初始化的數(shù)據(jù),在測試刪除操作時會遇到一些問題

20、。這里,我們引入Mock Objects。Mock Objects用來模擬外部復(fù)雜的資源(如數(shù)據(jù)庫,網(wǎng)絡(luò)連接等),使UI可以測試那些依賴于這些復(fù)雜外界資源的模塊。例如在測試一個跟數(shù)據(jù)庫有關(guān)系的模塊時,我們并不一定要建立一個真實(shí)的數(shù)據(jù)庫連接,而只需建立一個Mock Objects就可以了。測試所需的數(shù)據(jù)都存在于這個Mock Objects??梢哉f,Mock Objects為我們提供了一個輕量級的、可控制的、高效的模型。在本例中,影片的增加、刪除都會跟文件或數(shù)據(jù)庫操作發(fā)生關(guān)系。這時我們就可以利用Mock Objects來隔離測試代碼與文件或數(shù)據(jù)庫。使用Mock Objects一般有以下幾個步驟:a

21、)、定義一個外部資源的接口.(這個接口一般是可以在重構(gòu)過程中提煉出來的)。b)、定義一個Mock Objects,從外部資源的接口繼承下來,實(shí)現(xiàn)外部資源的接口。c)、創(chuàng)建一個Mock Objects,并設(shè)置它的內(nèi)部期望值。d)、把創(chuàng)建的這個Mock Objects傳遞給需要測試的模塊進(jìn)行操作。e)、操作完畢后將Mock Objects內(nèi)部的狀態(tài)與期待狀態(tài)比較。 現(xiàn)在我們就根據(jù)這個步驟來實(shí)現(xiàn)本例子中的Mock Objects.通過對前面的代碼進(jìn)行重構(gòu),我們可以提煉出一個接口MovieListEditor: class AFX_EXT_CLASS MovieListEditor public:Mo

22、vieListEditor();virtual MovieListEditor();public:virtual CStringArray* GetMovies()=0;virtual void Add(CString strMovie)=0;virtual void Delete(int nIndex)=0; 請注意它和前面我們定義的MovieListEditor的不同。接下來,我們應(yīng)該定義一個Mock Objects,當(dāng)然它是從MovieListEditor繼承下來的: class mockEditor : public MovieListEditorpublic: mockEditor(

23、); virtual mockEditor();public: virtual CStringArray* GetMovies()return &m_arMovieList; virtual void Add(CString strMovie)m_arMovieList.Add(strMovie); virtual void Delete(int nIndex)m_arMovieList.RemoveAt(nIndex);private: CStringArray m_arMovieList; 然后給這個Mock Objects設(shè)置初識值,我們選擇在它的構(gòu)造函數(shù)里進(jìn)行。 mockEdi

24、tor:mockEditor() m_arMovieList.Add("Star Wars"); m_arMovieList.Add("Star Trek"); m_arMovieList.Add("Stargate"); 我們添加了三個影片用于測試。接著,應(yīng)該把這個MockObjects的一個實(shí)例傳遞給需要測試的模塊。這里就是我們要測試的UI(MovieListWindow)。 m_pEditor = new mockEditor(); MovieListWindow *pWindow = new MovieListWindow(m

25、_pEditor); 最后我們來看看經(jīng)過修改后的新的測試添加影片的方法: void TestOperation:testAdd() /拷貝一份movie list CStringArray MovieNamesWithAddition; for(int n=0; n<m_MovieNames.GetSize(); n ) MovieNamesWithAddition.Add(m_MovieNames.GetAt(n); MovieNamesWithAddition.Add(LOST_IN_SPACE); /生成窗口 MovieListWindow *pWindow = new Movie

26、ListWindow(m_pEditor); pWindow->Init(); /填寫新的影片的名稱 CEdit* pEdit = pWindow->GetMovieField(); pEdit->SetWindowText(LOST_IN_SPACE); /點(diǎn)擊add btn CButton* pBtn = pWindow->GetAddButton(); :SendMessage(pBtn->m_hWnd, BM_CLICK, 0, 0); /檢查列表控件中是否已加入新的影片 CListBox* pListBox = pWindow->GetMovieL

27、istBox(); CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox->GetCount(); /將Mock Objects的內(nèi)部數(shù)據(jù)和期望值進(jìn)行比較 CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), m_pEditor->GetMovies()->GetSize(); /檢查列表控件中影片名是否正確 CString strNewMovieName; pListBox->GetText(pListBox->GetCount()-1, strNewMovieName); CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, strNewMovieName); /將Mock Objects的內(nèi)部數(shù)據(jù)和期望值進(jìn)行比較 int nIndex = m_pEditor->GetMovies()->GetSize(); CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, m_pEditor->GetMovies()-&g

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論