Keywords: Test Priority Test Drive Development Mock Objects CPPUnit1, Overview Test is preferred to test the core idea of Test-Driven Development (TDD), which requires the preparation of product code before writing product code. In the unit test of the test drive development, the automatic test of the GUI application should be one of the soft ribs that test driver development. Since the operation of the interface is done by the person, it is difficult to complete the unit automatic test in the GUI. Kent Beck has mentioned this issue in its "test driver development". This article will explain how unit tests for GUIs in test drive development through an example. This example is an example in the "Test Drive Development Practical Guide (Copyup version)" in David Astels. The Chinese version will be published in China. The book discusses and introduces a variety of ways to develop this example. The author will introduce one of them, and for easy access to friends with C , the code in the book has written it once with C , and the class name and variable name will be consistent with the original book to facilitate reading the book C reader. I would also like to thank David Astels to bring us so wonderful books. The background of this paper is: CPPUNIT 1.9.0, Visual C 6.0, Windows2000 Pro. The statement is incorrect in the article, please criticize and correct. If the reader has no understanding of CPPUnit, you can first refer to another article "Getting Started with the CPPUnit Test Framework". 2. Demand Analysis For this video management application, we mainly achieve, delete and display the functionality of the film list. Based on these needs, we can draw a GUI sketch, as shown in Figure 1: A list of controls that display all movies ListBox controls, a new film name EDIT control, an additional button control, one delete Button control. As a result, our development goals are very clear. 3. Write the UI test code this section of this part of this is mainly to test whether each control is generated correctly and is visible, and whether the Label text of some controls is correct. We inherit a class TestWidgets from TestCase for test windows and add four tests, tested Listbox, Edit, Add Button, delete button.
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;}; where, MovieListWindow is a window class. Let's take a look at one of the tests, please see the comments in the code.
void TestWidgets :: testAddButton () {// get pointer btn CButton * pAddButton = m_pWindow-> GetAddButton (); // check for generating btn CPPUNIT_ASSERT (pAddButton-> m_hWnd); // check btn is visible CPPUNIT_ASSERT_EQUAL (TRUE, :: IsWindowVisible (pAddButton-> m_hWnd)); CString strText; pAddButton-> GetWindowText (strText); CString strExpect = "Add"; Label text // check whether the correct btn CPPUNIT_ASSERT_EQUAL (strExpect, strText);} test code compilation, the compiler Will give us some error messages. This requires that we must write product code immediately to make compilation pass. First of all, the product code to be implemented is the MovieListWindow window class. 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 ()}; In the MovieListWindow window class we implements the required controls and some ways to these controls, such as getMovielistbox (), etc. This article does not detail here. Compile the test code and product code to check if it passes. Continue to check the product code for compilation and testing. 4. Writing control behavior test code will be written in writing to click on Add Button and Delete Button. Similarly, we inherited TestOperation from TestCase:
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;}; you will find that appear in TestOperation class A member variable Movielisteditor * m_peditor. Class MovieListeditor is a management class for saving movie data and increasing movie data, and deleting operations. Behind we will give its implementation. Look at Setup () What did: void testOperation :: setup () {/ Create a MOVIELISTEDITOR instance m_peditor = new movielisteditor (); m_movienames.removeall (); // copy the movie list in MovieListeditor to m_movienames, for the back Preparation for test for for (int N = 0; n
void TestOperation :: testAdd () {// copy movie list CStringArray MovieNamesWithAddition; for (int n = 0; n
CPPUNIT_ASSERT_EQUAL (MovienameSwithaddition.getsize (), PListbox-> getCount ()); test pass. After checking, know the reason is that we are in the test code:
:: SendMessage (PBTN-> M_HWND, BM_CLICK, 0, 0); Send the message to the Add Button, but in the MovieListWindow window we did not join the message response function, so the test did not pass. Hurry to add a message response function. void MovieListWindow :: OnClickAddButton () {UpdateData (); CString strNewMovieName; m_MovieField.GetWindowText (strNewMovieName); if (! "" = strNewMovieName) {m_pEditor-> Add (strNewMovieName); m_MovieListBox.AddString (strNewMovieName);}} compiler, Test, pass. 5, Mock Objects In the unit test of the delete operation, one of the problems we encounter is that the data of the film list should be saved in a text file or database, if we write test depending on these actual files or databases, Then our test will be subject to these external resources. Once the data in the file or database changes, it will inevitably affect our test code, resulting in an error test information. In the previous Movielisteditor, we did not add some initialized data, and some issues were encountered when testing delete operations. Here, we introduce Mock Objects. Mock Objects is used to simulate external complex resources (such as databases, network connections, etc.) so that the UI can test modules depend on these complex external resources. For example, when testing a module with a relationship with a database, we don't have to create a real database connection, and you can only create a Mock Objects. The data required for testing exists in this Mock Objects. It can be said that Mock Objects provides us with a lightweight, controlled, efficient model. In this example, the addition of the movie will have a relationship with the file or database operation. At this time we can use Mock Objects to isolate test code and files or databases. Using Mock Objects generally has the following steps: a), define an external resource interface. (This interface is generally extracted). b) Define a Mock Objects, inheriting from the interface of the external resource, and implement an external resource interface. c) Create a Mock Objects and set its internal expectations. D), pass the created Mock Objects to the module that needs to be tested. e) Compare the status of the Mock Objects and the expected state after the operation is completed. Now let's implement the Mock Objects in this example according to this step. By reconstructing the previous code, we can extract an interface Movielisteditor:
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;}; Please Note that it is different from the MovieListeditor we defined earlier. Next, we should define a mock objects, of course it is from 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;}; then set the primitive value to this Mock Objects, we choose to do in its constructor. Mockeditor :: mockeditor () {m_armovielist.add ("star wars"); m_armovielist.add ("star trek"); m_armovielist.add ("stargate");} We added three movies for testing. Next, an instance of this MockObjects should be passed to a module that needs to be tested. Here is the UI (MovieListWindow) we have to test.
m_peditor = new mockeditor (); movielistWindow * PWindow = new movielistWindow (m_peditor); Finally, let's see how to add a movie after modified new test:
void TestOperation :: testAdd () {// copy movie list CStringArray MovieNamesWithAddition; for (int n = 0; n
CPPUNIT_ASSERT_EQUAL (), m_peditor-> getMovies () -> getSize ()); cppunit_assert_equal (lost_in_space, m_peditor-> getMovies () -> getat (nindex-1)); other deleted operations Test and add similar, Not detailed herein. At this point, we have completed the development of this GUI application. All tests are shown in Figure 2: Figure 2 6, Source Code Description This article includes three Project, respectively, Movie, GuitestFirst, AppMovieList.Movie is a product code .GuitestFirst is a test code. AppMovielist is a application written by using the product code output by Movie, which inherits a new movie management class MyEditor from Movielisteditor. It is mainly to demonstrate how to use the Movielisteditor interface we extracted. For example, you can implement CxmlMovielisteditor, CaccessMovielisteditor, and more. Go to GuitestFirst to open all of these projects. AppMovielist is running as shown in Figure 3: Figure 3 7, summary A), a priority development method for the GUI application implementation test, which is not required in test drive development, can be selected according to the actual situation of development. b), we have enabled test code and external complex resources by introducing Mock Objects, and it also enables us to extract clear interfaces from both code to make the code clean and tidy. 8, Reference "Test Drive Development Practical Guide (Copy)" David Astels "test driver development (Chinese version)" Kent Beck "endo-testing: unit testing with mock objects" Tim Mackinnon, Steve Freeman, Philip Craig 9, Author contact details
Website: http://tdd.nease.net email: cpluser@hotmail.com blog: http://blog.9cbs.net/cpluser/