分类: C/C++
2008-08-07 17:40:06
class TestWidgets : public 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 testList(); 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是否可见 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 MovieListWindow : public CDialog { public: 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(); // 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(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: // Generated message map functions //{{AFX_MSG(MovieListWindow) //}}AFX_MSG DECLARE_MESSAGE_MAP() };在MovieListWindow窗口类中我们实现了需要的控件以及针对这些控件的一些方法,如GetMovieListBox()等,本文在此不做详述。编译测试代码和产品代码,检查是否通过。如未通过则继续检查产品代码以使编译和测试通过。
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(); 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; };你会发现,在TestOperation类中出现了一个成员变量MovieListEditor* m_pEditor。类MovieListEditor是一个用来保存影片数据以及对影片数据进行增加 ,删除操作的管理类。后面我们会给出它的实现。 看看setUp()做了什么:
void TestOperation::setUp() { //创建一个MovieListEditor实例 m_pEditor = new MovieListEditor(); m_MovieNames.RemoveAll(); //将MovieListEditor中的影片列表拷贝到m_MovieNames,为后面测试作准备 for(int n=0; n我们来看看添加影片的测试,请看代码注释:GetMovies()->GetSize(); n ) { m_MovieNames.Add(m_pEditor->GetMovies()->GetAt(n)); } }
void TestOperation::testAdd() { //拷贝一份movie list CStringArray MovieNamesWithAddition; for(int n=0; n编译后会有出错信息,主要的错误有:Init(); //填写新的影片的名称 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->GetMovieListBox(); CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox->GetCount()); //检查列表控件中影片名是否正确 CString strNewMovieName; pListBox->GetText(pListBox->GetCount()-1, strNewMovieName); CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, strNewMovieName); //销毁窗口 pWindow->DestroyWindow(); delete pWindow; pWindow = NULL; }
class AFX_EXT_CLASS MovieListEditor { public: MovieListEditor(); virtual ~MovieListEditor(); 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; };再次编译,已经通过.运行测试,发现在:
CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), pListBox->GetCount());测试通不过。检查后知道原因是,我们在测试代码里:
::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->Add(strNewMovieName); m_MovieListBox.AddString(strNewMovieName); } }编译、测试、通过。
class AFX_EXT_CLASS MovieListEditor { public: MovieListEditor(); virtual ~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 MovieListEditor { public: mockEditor(); 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设置初识值,我们选择在它的构造函数里进行。
mockEditor::mockEditor() { m_arMovieList.Add("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() { //拷贝一份movie list CStringArray MovieNamesWithAddition; for(int n=0; n请注意,这里测试的数据都是mockEditor里的,而且在UI进行添加操作后,还将mockEditor内部的状态与期待状态做了比较。Init(); //填写新的影片的名称 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->GetMovieListBox(); 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, strNewMovieName); 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; }
CPPUNIT_ASSERT_EQUAL(MovieNamesWithAddition.GetSize(), m_pEditor->GetMovies()->GetSize()); CPPUNIT_ASSERT_EQUAL(LOST_IN_SPACE, m_pEditor->GetMovies()->GetAt(nIndex-1));其他删除操作的测试跟添加类似,在此不做详述。至此,我们就完成了这个GUI应用程序的开发。所有的测试如图2所示:
9、作者联系方式