




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、使用測(cè)試優(yōu)先方法開發(fā)用戶界面 本文作者 未知 摘自 機(jī)電之家 1、概述測(cè)試優(yōu)先是測(cè)試驅(qū)動(dòng)開發(fā)(Test-Driven Development, TDD)的核心思想,它要求在編寫產(chǎn)品代碼前先編寫基于產(chǎn)品代碼的測(cè)試代碼。在測(cè)試驅(qū)動(dòng)開發(fā)的單元測(cè)試中,對(duì)GUI應(yīng)用實(shí)施自動(dòng)測(cè)試應(yīng)該是測(cè)試驅(qū)動(dòng)開發(fā)的軟肋之一。由于界面的操作是有由人來(lái)完成的,所以要想在GUI中完成單元自動(dòng)測(cè)試是有一定難度的。Kent Beck在它的測(cè)試驅(qū)動(dòng)開發(fā)中就曾提到過(guò)這個(gè)問(wèn)題。本文將通過(guò)一個(gè)例子來(lái)講解在測(cè)試驅(qū)動(dòng)開發(fā)中如何針對(duì)GUI進(jìn)行單元測(cè)試。這個(gè)例子是 David Astels著的測(cè)試驅(qū)動(dòng)開發(fā)實(shí)用指南(影印版)中一個(gè)關(guān)于影片列表管理的
2、例子。該書中文版即將在國(guó)內(nèi)出版。書中討論并介紹了開發(fā)這個(gè)例子的多種方法。筆者將介紹其中的一種,并且為了方便使用C 的朋友的學(xué)習(xí),書中的代碼我用C 寫了一遍,類名和變量名盡量和原書保持一致,以方便閱讀該書的C 讀者。在此也要感謝David Astels給我們帶來(lái)如此精彩的一本書。本文敘述背景為:CppUnit1.9.0, Visual C 6.0, Windows2000 pro。文中敘述有誤之處,敬請(qǐng)批評(píng)指正。如果讀者對(duì)CppUnit還沒(méi)有一定的了解,可以先參考筆者的另一篇文章CppUnit測(cè)試框架入門。2、需求分析對(duì)于這個(gè)影片管理的應(yīng)用,我們主要實(shí)現(xiàn)增加、刪除和顯示影片列表的功能?;谶@些需
3、求,我們可以畫一張GUI草圖。界面的控件主要有:一個(gè)顯示所有影片的列表listbox控件,一個(gè)填寫新的影片名的edit控件,一個(gè)增加button控件,一個(gè)刪除button控件。由此,我們的開發(fā)目標(biāo)就十分的明確了。3、編寫UI測(cè)試代碼這部分的UI測(cè)試代碼主要是測(cè)試各個(gè)控件是否正確生成并且是可見的,以及測(cè)試一些控件的label文字是否正確。我們從TestCase繼承一個(gè)類TestWidgets用于測(cè)試窗口,并添加四個(gè)測(cè)試,分別測(cè)試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是一個(gè)窗口類。我們來(lái)看看其中的一個(gè)測(cè)試,請(qǐng)看代碼中的注釋。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);編譯測(cè)試代碼,編譯器會(huì)給我們一些出錯(cuò)信息。這要求我們必須馬上編寫產(chǎn)品代碼以讓編譯通過(guò)。首先第一個(gè)要實(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)了需要的控件以及針對(duì)這些控件的一些方法,如GetMovieListBox()等,本文在此不做詳述。編譯測(cè)試代碼和產(chǎn)品代碼,檢查是否通過(guò)。如未通過(guò)則繼續(xù)檢查產(chǎn)品代碼以使編譯和測(cè)試通過(guò)。 4
10、、編寫控件行為測(cè)試代碼 接下來(lái)應(yīng)該是編寫點(diǎn)擊add button和delete button的測(cè)試代碼了。同樣,我們從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; 你會(huì)發(fā)現(xiàn),在TestOperation類中出現(xiàn)了一個(gè)成員變量MovieListEditor* m_pEditor。類MovieLi
12、stEditor是一個(gè)用來(lái)保存影片數(shù)據(jù)以及對(duì)影片數(shù)據(jù)進(jìn)行增加,刪除操作的管理類。后面我們會(huì)給出它的實(shí)現(xiàn)??纯磗etUp()做了什么: void TestOperation:setUp() /創(chuàng)建一個(gè)MovieListEditor實(shí)例 m_pEditor = new MovieListEditor(); m_MovieNames.RemoveAll(); /將MovieListEditor中的影片列表拷貝到m_MovieNames,為后面測(cè)試作準(zhǔn)備 for(int n=0; n<m_pEditor->GetMovies()->GetSize(); n ) m_MovieName
13、s.Add(m_pEditor->GetMovies()->GetAt(n); 我們來(lái)看看添加影片的測(cè)試,請(qǐ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); /生成窗口 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; 編譯后會(huì)有出錯(cuò)信息,主要的錯(cuò)誤有:a)、我們把m_pEditor保存在MovieListWindow中了,這需要我們修改原來(lái)的MovieListWindow的構(gòu)造函數(shù)。 b)、沒(méi)有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)通過(guò).運(yùn)行測(cè)試,發(fā)現(xiàn)在: CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox->GetCount(); 測(cè)試通不
18、過(guò)。檢查后知道原因是,我們?cè)跍y(cè)試代碼里: :SendMessage(pBtn->m_hWnd, BM_CLICK, 0, 0); 給add button發(fā)送了點(diǎn)擊按鈕的消息,但是在MovieListWindow 窗口中我們沒(méi)有加入消息的響應(yīng)函數(shù),因此測(cè)試沒(méi)有通過(guò)。趕緊添加消息響應(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); 編譯、測(cè)試、通過(guò)。 5、Mock Objects 在刪除操作的單元測(cè)試中,我們遇到的一個(gè)問(wèn)題是,影片列表的數(shù)據(jù)應(yīng)該是保存在一個(gè)文本文件或者數(shù)據(jù)庫(kù)當(dāng)中的,如果我們編寫的測(cè)試依賴于這些實(shí)際的文件或數(shù)據(jù)庫(kù),那么我們的測(cè)試就會(huì)受制于這些外部的資源。一旦文件或者數(shù)據(jù)庫(kù)里的數(shù)據(jù)發(fā)生變化,必然會(huì)波及到我們的測(cè)試代碼,從而產(chǎn)生錯(cuò)誤的測(cè)試信息。前面的MovieListEditor中我們沒(méi)有加入一些初始化的數(shù)據(jù),在測(cè)試刪除操作時(shí)會(huì)遇到一些問(wèn)題
20、。這里,我們引入Mock Objects。Mock Objects用來(lái)模擬外部復(fù)雜的資源(如數(shù)據(jù)庫(kù),網(wǎng)絡(luò)連接等),使UI可以測(cè)試那些依賴于這些復(fù)雜外界資源的模塊。例如在測(cè)試一個(gè)跟數(shù)據(jù)庫(kù)有關(guān)系的模塊時(shí),我們并不一定要建立一個(gè)真實(shí)的數(shù)據(jù)庫(kù)連接,而只需建立一個(gè)Mock Objects就可以了。測(cè)試所需的數(shù)據(jù)都存在于這個(gè)Mock Objects。可以說(shuō),Mock Objects為我們提供了一個(gè)輕量級(jí)的、可控制的、高效的模型。在本例中,影片的增加、刪除都會(huì)跟文件或數(shù)據(jù)庫(kù)操作發(fā)生關(guān)系。這時(shí)我們就可以利用Mock Objects來(lái)隔離測(cè)試代碼與文件或數(shù)據(jù)庫(kù)。使用Mock Objects一般有以下幾個(gè)步驟:a
21、)、定義一個(gè)外部資源的接口.(這個(gè)接口一般是可以在重構(gòu)過(guò)程中提煉出來(lái)的)。b)、定義一個(gè)Mock Objects,從外部資源的接口繼承下來(lái),實(shí)現(xiàn)外部資源的接口。c)、創(chuàng)建一個(gè)Mock Objects,并設(shè)置它的內(nèi)部期望值。d)、把創(chuàng)建的這個(gè)Mock Objects傳遞給需要測(cè)試的模塊進(jìn)行操作。e)、操作完畢后將Mock Objects內(nèi)部的狀態(tài)與期待狀態(tài)比較。 現(xiàn)在我們就根據(jù)這個(gè)步驟來(lái)實(shí)現(xiàn)本例子中的Mock Objects.通過(guò)對(duì)前面的代碼進(jìn)行重構(gòu),我們可以提煉出一個(gè)接口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; 請(qǐng)注意它和前面我們定義的MovieListEditor的不同。接下來(lái),我們應(yīng)該定義一個(gè)Mock Objects,當(dāng)然它是從MovieListEditor繼承下來(lái)的: 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; 然后給這個(gè)Mock Objects設(shè)置初識(shí)值,我們選擇在它的構(gòu)造函數(shù)里進(jìn)行。 mockEdi
24、tor:mockEditor() m_arMovieList.Add("Star Wars"); m_arMovieList.Add("Star Trek"); m_arMovieList.Add("Stargate"); 我們添加了三個(gè)影片用于測(cè)試。接著,應(yīng)該把這個(gè)MockObjects的一個(gè)實(shí)例傳遞給需要測(cè)試的模塊。這里就是我們要測(cè)試的UI(MovieListWindow)。 m_pEditor = new mockEditor(); MovieListWindow *pWindow = new MovieListWindow(m
25、_pEditor); 最后我們來(lái)看看經(jīng)過(guò)修改后的新的測(cè)試添加影片的方法: 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. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 縫紉輔助設(shè)備項(xiàng)目風(fēng)險(xiǎn)評(píng)估報(bào)告
- 便民小屋項(xiàng)目合同范本
- 2023-2029年中國(guó)黑龍江省智慧交通行業(yè)市場(chǎng)發(fā)展現(xiàn)狀及投資策略咨詢報(bào)告
- 住宅輕工勞務(wù)合同范本
- 代建合同范本 標(biāo)準(zhǔn)范本
- 2025年電工用銅線材項(xiàng)目可行性研究報(bào)告
- 微信內(nèi)容推送計(jì)劃
- 科技創(chuàng)新引領(lǐng)的未來(lái)科普發(fā)展趨勢(shì)
- 代工手機(jī)合同范本
- 公務(wù)車輛購(gòu)置合同范本
- 動(dòng)力電池包pack控制計(jì)劃
- 養(yǎng)老機(jī)構(gòu)員工考核表
- 臟腑辨證與護(hù)理
- 外科洗手、消毒、鋪巾講座課件
- 《小型局域網(wǎng)構(gòu)建》一體化課程標(biāo)準(zhǔn)
- 甲基丙烯酸甲酯生產(chǎn)工藝畢業(yè)設(shè)計(jì)設(shè)備選型與布置模板
- 單肺通氣策略
- dd5e人物卡可填充格式角色卡夜版
- RT Thread設(shè)備驅(qū)動(dòng)開發(fā)指南
- 高一第二學(xué)期英語(yǔ)教學(xué)計(jì)劃進(jìn)度表
- 走中國(guó)工業(yè)化道路的思想及成就
評(píng)論
0/150
提交評(píng)論