Use test priority to develop user interface

xiaoxiao2021-03-06  67

Use test priority to develop user interface

Author: cpluser Website: http://www.enjoyimage.ComeMail: enjoyimage@163.comblog: http://blog.9cbs.net/cpluser/ Demo Code Download

Keywords: Test Priority Test Driven Development Mock Objects CPPUnit 1, Overview Test is preferred to test the core idea of ​​Test-Driven Development, TDD, which requires the product code based test 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:

figure 1

The controls of the interface mainly include: a list of lists of all movies, an Edit control that fills in a new film name, an additional button control, a 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"; // Check if btn's Label text correct cppunit_assert_equal (strers);} Compile test code, 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 methods for these controls, such as getMovieListbox (), etc., this article does not detail herein. 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 inherit from TestCase out 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 (); 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 a member variable Movielisteditor * m_peditor is appearing in the TestOperation class. Class MovieListeditor is a management class for saving movie data and increasing movie data. Behind we will give it an implementation. Take a look Setup () did, Void Testoperation :: setup () {// creates a MOVIELISTEDITOR instance m_peditor = new movielisteditor (); m_movienames.removell (); // copy the movie list in MovieListeditor to m_movienames, for the back test Prepare for (int N = 0; n getmovies () -> getSize (); n ) {m_movienames.add (m_peditor-> getmovies () -> getat (n));

}} Let's take a look at the test to add a film. Void Testoperation :: testAdd () {// Copy a Movie List CStringArray MovienameSwithaddition; for (int n = 0; n Init (); // fill a new video Name CEDIT * PEDIT = PWindow-> getMovief (); pedit-> setWindowText (lost_in_space); // Click Add BTN CButton * PBTN = PWindow-> getAddButton () (); :: SendMessage (PBTN-> M_HWND, BM_CLICK, 0, 0 ); // Check if there is a new movie clistbox * plistbox = PWindow-> getMovielistbox (); cppUnit_aswithaddition.get (), PListbox-> getCount ()); // Check whether the film name in the list control is checked. correct CString strNewMovieName; pListBox-> GetText (pListBox-> GetCount () - 1, strNewMovieName); CPPUNIT_ASSERT_EQUAL (LOST_IN_SPACE, strNewMovieName); // destroy the window pWindow-> DestroyWindow (); delete pWindow; pWindow = NULL; } There will be error messages after compiling, and the main mistakes are: a) Save M_PEDITOR in MovielistWindow, which requires us to modify the original MovieListWindow constructor. B) No Movielisteditor class. Movielisteditor implementation is as follows: Class AFX_EXT_CLASS MOVIELISTEDITEDITEG 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 ;

}; Again compiled, it has passed. Run test, found in cppunit_assert_equal (movienameswithaddition.get (), PListbox-> getCount ()); test nothing. After checking the reason, we are in test code :: SendMessage (PBTN- > m_hwnd, bm_click, 0, 0); send the message to the Add Button, but we did not join the message response function in the MovieListWindow window, 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);}} compile, test through 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 have written test depending on these actual files or databases, then we The 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 join some initialized data, and some problems 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) Defines a Mock Objects, inheriting from the external resource interface, Implement an external resource interface. c) Create a Mock Objects and set its internal expected value. 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 we have achieved in accordance with this step Mock Objects of the present example 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;}; note that it is different from the MovieListeditor of our definition. Next, we should define one . mock Objects, of course, it is inherited from MovieListEditor down 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 give this Mock Objects to initiate a primitive value, we choose it in it In the constructor. Mockeditor :: mockditor () {m_armovielist.add ("star wars"); m_armovielist.add ("star TREK"); m_armovielist.add ("stargate");

} We added three films for testing. Next, an instance of this MockObjects should be passed to modules that need to be tested. This is the UI (MovielistWindow) we want to test. M_peditor = new mockeditor (); MovielistWindow * PWindow = New MovielistWindow (M_Peditor); Finally, let's take a look at the modified new test to add a movie, void test :: testAdd () {// Copy a Movie List CStringArray MovienameSwithaddition; for (int N = 0; n Init (); / / Fill in the name of the new film CEDIT * PEDIT = PWindow-> getMoviefield (); pedit-> setWindowText (LOST_IN_SPACE); // Click Add BTN CButton * PBTN = PWindow-> getAddButton (); :: SendMessage (PBTN-> m_hwnd , BM_Click, 0, 0); // Check that the list control has been added to the new movie clistbox * plistbox = PWindow-> getMovielistbox (); cppunit_assert_equal (movienameswithaddition.getsize (), PListbox-> getCount ()); // The internal data and expectations of Mock Objects are compared to CPPUNIT_ASSERT_EQUAL (MovienameSwithaddi) Tion.getsize (), m_peditor-> getmovies () -> getSize ()); // Check if the film name in the list control is correct CString strnewmoviename; PListbox-> getText () - 1, strnewmoviename); cppUnit_assert_equal (LOST_IN_SPACE, strNewMovieName); // internal data and the expected value is compared to Mock Objects int nIndex = m_pEditor-> GetMovies () -> GetSize (); CPPUNIT_ASSERT_EQUAL (LOST_IN_SPACE, m_pEditor-> GetMovies () -> GetAt (nIndex-1 )); // Destroy window PWindow-> DestroyWindow (); delete PWindow; PWindow = null;} Please note that the data tested here is mockeditor, and after the UI is added, the state is also in the middle of the mockeditor. The expected state has been compared. CPPUnit_assert_equal (movienameswithaddition.getsize (), m_peditor-> getmovies () -> getSize ());

CPPUNIT_ASSERT_EQUAL (LOST_IN_SPACE, M_PEDITOR-> getMovies () -> getat (nindex-1)); Tests for other delete operations is similar, not detail here. To this, we have completed the development of this GUI application. The test is shown in Figure 2: Figure 2

6, Source Code Description This article includes three Project, respectively, GuitestFirst, AppMovielist.Movie is product code .GuitstFirst is a test code .AppMovielist is a application written using the product code output by Movie, which inherits from Movielisteditor A new film management class MyEditor. It is mainly to demonstrate how to use the Movielisteditor interface we extracted. For example, you can implement CXMLMOVIELISTEDITOR, CACCESSMOVILISTEDITOR, and more. Go to GuitestFirst to open all of these projects. AppMovielist is shown in Figure 3:

image 3

7. Summary A) Priority development method for the implementation of the GUI application, which is not necessary in test drive development, and can be selected according to the actual situation of development. b) By introducing Mock Objects, we have left the test code and the external complex resource separated, and it also enables us to extract a clear interface from both code to make the code clean and tidy. 8, reference information David Astels Kent Beck Tim Mackinnon, Steve Freeman, Philip CRAIG

转载请注明原文地址:https://www.9cbs.com/read-91765.html

New Post(0)