




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、使用测试优先方法开发用户界面关键字:测试优先 测试驱动开发 Mock Objects CppUnit1、概述测试优先是测试驱动开发(Test-Driven Development, TDD)的核心思想,它要求在编写产品代码前先编写基于产品代码的测试代码。在测试驱动开发的单元测试中,对GUI应用实施自动测试应该是测试驱动开发的软肋之一。由于界面的操作是有由人来完成的,所以要想在GUI中完成单元自动测试是有一定难度的。Kent Beck在它的测试驱动开发中就曾提到过这个问题。本文将通过一个例子来讲解在测试驱动开发中如何针对GUI进行单元测试。这个例子是David Astels著的测试驱动开发实用指
2、南(影印版)中一个关于影片列表管理的例子。该书中文版即将在国内出版。书中讨论并介绍了开发这个例子的多种方法。笔者将介绍其中的一种,并且为了方便使用C+的朋友的学习,书中的代码我用C+写了一遍,类名和变量名尽量和原书保持一致,以方便阅读该书的C+读者。在此也要感谢David Astels给我们带来如此精彩的一本书。本文叙述背景为:CppUnit1.9.0, Visual C+ 6.0, Windows2000 pro。文中叙述有误之处,敬请批评指正。如果读者对CppUnit还没有一定的了解,可以先参考笔者的另一篇文章CppUnit测试框架入门。2、需求分析对于这个影片管理的应用,我们主要实现增加
3、、删除和显示影片列表的功能。基于这些需求,我们可以画一张GUI草图,如图1: 图1界面的控件主要有:一个显示所有影片的列表listbox控件,一个填写新的影片名的edit控件,一个增加button控件,一个删除button控件。由此,我们的开发目标就十分的明确了。3、编写UI测试代码这部分的UI测试代码主要是测试各个控件是否正确生成并且是可见的,以及测试一些控件的label文字是否正确。我们从TestCase继承一个类TestWidgets用于测试窗口,并添加四个测试,分别测试listbox、edit、add button、delete button。class TestWidgets : p
4、ublic CppUnit:TestCase CPPUNIT_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 test
5、List(); void testField(); 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是否可
6、见 CPPUNIT_ASSERT_EQUAL(TRUE, :IsWindowVisible(pAddButton-m_hWnd); CString strText; pAddButton-GetWindowText(strText); CString strExpect = Add; /检查btn的Label文字是否正确 CPPUNIT_ASSERT_EQUAL(strExpect, strText);编译测试代码,编译器会给我们一些出错信息。这要求我们必须马上编写产品代码以让编译通过。首先第一个要实现的产品代码就是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; void Init(); /
8、 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 DoDataExchange(CDat
9、aExchange* pDX); / DDX/DDV support /AFX_VIRTUAL / Implementation protected: / Generated message map functions /AFX_MSG(MovieListWindow) /AFX_MSG DECLARE_MESSAGE_MAP();在MovieListWindow窗口类中我们实现了需要的控件以及针对这些控件的一些方法,如GetMovieListBox()等,本文在此不做详述。编译测试代码和产品代码,检查是否通过。如未通过则继续检查产品代码以使编译和测试通过。 4、编写控件行为测试代码 接下来应
10、该是编写点击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(); void testAdd(); vo
11、id testDelete();public: void setUp(); void tearDown(); TestOperation(); virtual TestOperation();private: static CString LOST_IN_SPACE; CStringArray m_MovieNames; MovieListWindow* m_pWindow; MovieListEditor* m_pEditor;你会发现,在TestOperation类中出现了一个成员变量MovieListEditor* m_pEditor。类MovieListEditor是一个用来保存影片数
12、据以及对影片数据进行增加 ,删除操作的管理类。后面我们会给出它的实现。看看setUp()做了什么:void TestOperation:setUp() /创建一个MovieListEditor实例 m_pEditor = new MovieListEditor(); m_MovieNames.RemoveAll(); /将MovieListEditor中的影片列表拷贝到m_MovieNames,为后面测试作准备 for(int n=0; nGetMovies()-GetSize(); n+) m_MovieNames.Add(m_pEditor-GetMovies()-GetAt(n); 我们
13、来看看添加影片的测试,请看代码注释:void TestOperation:testAdd() /拷贝一份movie list CStringArray MovieNamesWithAddition; for(int n=0; nInit(); /填写新的影片的名称 CEdit* pEdit = pWindow-GetMovieField(); pEdit-SetWindowText(LOST_IN_SPACE); /点击add btn CButton* pBtn = pWindow-GetAddButton(); :SendMessage(pBtn-m_hWnd, BM_CLICK, 0, 0
14、); /检查列表控件中是否已加入新的影片 CListBox* pListBox = pWindow-GetMovieListBox(); CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox-GetCount(); /检查列表控件中影片名是否正确 CString strNewMovieName; pListBox-GetText(pListBox-GetCount()-1, strNewMovieName); CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, strNewMovieName); /销
15、毁窗口 pWindow-DestroyWindow(); delete pWindow; pWindow = NULL;编译后会有出错信息,主要的错误有:a)、我们把m_pEditor保存在MovieListWindow中了,这需要我们修改原来的MovieListWindow的构造函数。 b)、没有MovieListEditor类。MovieListEditor的实现如下:class AFX_EXT_CLASS MovieListEditor public: MovieListEditor(); virtual MovieListEditor();public: virtual CString
16、Array* 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;再次编译,已经通过.运行测试,发现在:CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox-GetCount();测试通不
17、过。检查后知道原因是,我们在测试代码里::SendMessage(pBtn-m_hWnd, BM_CLICK, 0, 0);给add button发送了点击按钮的消息,但是在MovieListWindow 窗口中我们没有加入消息的响应函数,因此测试没有通过。赶紧添加消息响应函数。void MovieListWindow:OnClickAddButton() UpdateData(); CString strNewMovieName; m_MovieField.GetWindowText(strNewMovieName); if( != strNewMovieName) m_pEditor-Ad
18、d(strNewMovieName); m_MovieListBox.AddString(strNewMovieName); 编译、测试、通过。 5、Mock Objects 在删除操作的单元测试中,我们遇到的一个问题是,影片列表的数据应该是保存在一个文本文件或者数据库当中的,如果我们编写的测试依赖于这些实际的文件或数据库,那么我们的测试就会受制于这些外部的资源。一旦文件或者数据库里的数据发生变化,必然会波及到我们的测试代码,从而产生错误的测试信息。前面的MovieListEditor中我们没有加入一些初始化的数据,在测试删除操作时会遇到一些问题 。这里,我们引入Mock Objects。Mo
19、ck Objects用来模拟外部复杂的资源(如数据库,网络连接等),使UI可以测试那些依赖于这些复杂外界资源的模块。例如在测试一个跟数据库有关系的模块时,我们并不一定要建立一个真实的数据库连接,而只需建立一个Mock Objects就可以了。测试所需的数据都存在于这个Mock Objects。可以说,Mock Objects为我们提供了一个轻量级的、可控制的、高效的模型。在本例中,影片的增加、删除都会跟文件或数据库操作发生关系。这时我们就可以利用Mock Objects来隔离测试代码与文件或数据库。使用Mock Objects一般有以下几个步骤:a)、定义一个外部资源的接口.(这个接口一般是可
20、以在重构过程中提炼出来的)。b)、定义一个Mock Objects,从外部资源的接口继承下来,实现外部资源的接口。c)、创建一个Mock Objects,并设置它的内部期望值。d)、把创建的这个Mock Objects传递给需要测试的模块进行操作。e)、操作完毕后将Mock Objects内部的状态与期待状态比较。 现在我们就根据这个步骤来实现本例子中的Mock Objects.通过对前面的代码进行重构,我们可以提炼出一个接口MovieListEditor:class AFX_EXT_CLASS MovieListEditor public:MovieListEditor();virtual
21、MovieListEditor();public:virtual CStringArray* GetMovies()=0;virtual void Add(CString strMovie)=0;virtual void Delete(int nIndex)=0;请注意它和前面我们定义的MovieListEditor的不同。接下来,我们应该定义一个Mock Objects,当然它是从MovieListEditor继承下来的:class mockEditor : public MovieListEditorpublic: mockEditor(); virtual mockEditor();pu
22、blic: 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设置初识值,我们选择在它的构造函数里进行。mockEditor:mockEditor() m_arMovieList.A
23、dd(Star Wars); m_arMovieList.Add(Star Trek); m_arMovieList.Add(Stargate);我们添加了三个影片用于测试。接着,应该把这个MockObjects的一个实例传递给需要测试的模块。这里就是我们要测试的UI(MovieListWindow)。m_pEditor = new mockEditor(); MovieListWindow *pWindow = new MovieListWindow(m_pEditor);最后我们来看看经过修改后的新的测试添加影片的方法:void TestOperation:testAdd() /拷贝一份m
24、ovie list CStringArray MovieNamesWithAddition; for(int n=0; nInit(); /填写新的影片的名称 CEdit* pEdit = pWindow-GetMovieField(); pEdit-SetWindowText(LOST_IN_SPACE); /点击add btn CButton* pBtn = pWindow-GetAddButton(); :SendMessage(pBtn-m_hWnd, BM_CLICK, 0, 0); /检查列表控件中是否已加入新的影片 CListBox* pListBox = pWindow-Get
25、MovieListBox(); CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox-GetCount(); /将Mock Objects的内部数据和期望值进行比较 CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), m_pEditor-GetMovies()-GetSize(); /检查列表控件中影片名是否正确 CString strNewMovieName; pListBox-GetText(pListBox-GetCount()-1, strNewMovie
26、Name); CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, strNewMovieName); /将Mock Objects的内部数据和期望值进行比较 int nIndex = m_pEditor-GetMovies()-GetSize(); CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, m_pEditor-GetMovies()-GetAt(nIndex-1); /销毁窗口 pWindow-DestroyWindow(); delete pWindow; pWindow = NULL;请注意,这里测试的数据都是mockEditor里的,而且在UI进行添加操作后,还将mockEditor内部的状态与期待状态做了比较。CPPUNIT_ASSERT_E
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五版航空航天材料承包合同
- 二零二五年度跨境贸易代理合同参考
- 2025年木材夹板产销一体化合同范本
- 二零二五年度护工服务与福利合同范本
- 二零二五年度金融信用评估保密协议范本
- 二零二五版公司安防设备升级改造合同
- 二零二五年度房地产业劳动合同范本:绿色建筑项目员工职业素养培训及劳动合同
- 2025年装配钳工(高级)考试试卷:装配钳工技术创新与行业应用案例
- 剑桥PET2025年试卷语法知识系统梳理
- 2025年高考数学模拟试卷:立体几何突破难题解析与实战试题
- 湖南省长沙市岳麓区长郡双语实验中学2024-2025学年八年级下学期期末语文试题(含答案)
- 面向人体介入器械的电磁定位方法研究
- 二零二五年度木材加工产品买卖合同
- 儿科肺炎健康教育
- 新消费系列报告:解构“第五消费时代”-民生证券
- 供应商物流管理办法规定
- 公司承包责任制管理办法
- 加油站安全操作规程及安全管理制度
- 2025年河北 高考真题化学试题(解析版)
- 2025消防安全知识培训试题及答案
- 沥青路面施工现场文明施工措施
评论
0/150
提交评论