Welcome!

PowerBuilder Authors: Chris Pollach, Yeshim Deniz, Jayaram Krishnaswamy, Kevin Benedict, Avi Rosenthal

Related Topics: PowerBuilder, Microsoft Cloud

PowerBuilder: Article

Refactoring PowerBuilder Applications

Rolling multiple test cases into a single test suite

The migration march to PB 12.NET will have many shops revisiting legacy applications. In my previous articles ("Refactoring Is Not an ‘R' Word, [PBDJ, Vol. 16, issue 12] and "Refactoring Classic PowerBuilder Applications Using TDD and pbUnit" [PBDJ, Vol. 17, issue 1) you read why refactoring code before migration helps ensure smooth migration and enterprise integration. You were introduced to Test Driven Development methodology and saw how you can use it to ensure successful refactoring. I introduced you to pbUnit, an open source tool and framework that you can use for both refactoring and developing new code in PB Classic applications, and guided you through installing pbUnit and mastering the basic algorithm for refactoring PB legacy code. In this article I'll show you a technique to use for rolling multiple test cases into a single test suite and how to approach writing test cases that exercise database-centric code.

Merging Common Code into a Test Suite
At the conclusion of my last article I guided you to write three test cases that characterized the behavior of the calculator application under three different sets of input values: two positives, a positive and a negative, and two zero values. After writing these three tests, a discerning programmer would have noticed that two sections of code were common to and repeated in each of the three tests: the initial setup code and the concluding tear down code. No doubt an astute object-oriented programmer would wonder: Could I have written these code segments one time? Could they have been automatically invoked at the proper time for every test in the series? For those who are just reading along, here's one test case with the common code in bold face:

//(1) Setting up to do the test
w_calctester lw_calctester // create the window in memory so you can get at its members
lw_calctester = Create w_calctester

//(2) Set up specifics for this test and execute it
lw_calctester.sle_years.text ='5'                                  //provide positive inputs
lw_calctester.sle_amount.text = '50000'                     //provide positive inputslw_calctester.cb_calc.triggerevent(clicked!)   //execute the method
//(3) Compare the results of the run against expected results
this.assert( 'Positive Value Incorrectly Calculated', &
lw_calctester.sle_interest_paid.text ='$250,000.00' )
//(4) Tear down the setup to clean up memory after the test.

destroy lw_calctester

Granted this code is trivial, but in most production quality applications you will have many more steps to build, and to set the state for the complex memory structures you need to support the method under test; ditto for the code you need to clean up after a test run. Wouldn't it be useful to write the code once and have it automatically invoked?

For this purpose, the pbUnit TestCase ancestor provides two "life cycle" events: SetUp and TearDown. pbUnit invokes these events automatically before/after every test event call in a test case. You write code to build your memory structures in the SetUp event and write code to destroy them in the TearDown event. pbUnit hands each test event its own isolated and individual memory structure, so there is no chance of having a "dirty" test structure. Figures 1-4 show what the test case code will look like after refactoring the test.

Figure 1

Figure 2

Figure 3

Figure 4: Test Case with SetUp and TearDown

Test Suite: Executing Multiple Test Cases in a Single Run
After a while the number of TestCase objects in your collection will grow. You will find that you need to run several test cases as a sequence to cover a particular area of code. You might find yourself wishing that you could group multiple tests together and automatically run them as a sequence. pbUnit provides a way for you to group TestCases together using a TestSuite class. Internally, pbUnit gathers all tests into a virtual TestSuite and runs them from there, calling SetUp and TearDown from each test to guarantee isolation. You can formalize the TestSuite and use it to your advantage. To construct a TestSuite, inherit from the TestSuite class in the constructor event for each test you wish to invoke the Initialize method, passing the testcase class name as a string parameter. Save the test suite class, giving it a name that starts with the word "Suite" so you can recognize it in the GUI. For the sake of clarity, organization and ease of use, you might create a library just for your suites. To run the entire suite as a sequence, select the suite from your test list and run it (see Figures 5, 6 and 7).

Figure 5

Figure 6: TestSuite constructor code

Figure 7: Running a TestSuite

To Mock or Not to Mock: Testing DB-Centric Code
TDD purists claim that tests that exercise database access code are not really unit tests, since they depend on an external entity. A purist would take the approach of building a mock data object that returns the data that would have been returned from a call to the database and in the test replacing the call to the database with a call to the mock object. Pragmatists, however, recognize that testing data access code can be valuable because exercising classes that interact directly with the database can guarantee these classes execute the right SQL statements, assemble the right objects, and so on. Although these tests depend on external entities, they exercise classes that are the basic building blocks of a big application. One important caveat is that you must ensure the database is in a known state before you run your tests. Another limiting factor is that for unit tests to be useful, they must run fast; database access can be relatively slow. In the Java world there is a robust framework called dbUnit that deals with the issues of database access testing. Currently there is no such framework for PowerBuilder. See Chapter 16 of jUnit in Action for a complete discussion of database testing with jUnit.

If you are going to test data access code using pbUnit, you must be aware that since each test case runs in isolation from all others, a connection established globally using SQLCA will not be available to your test code. One approach would be to establish a connection inside each test case using the SetUp and TearDown events. However, you can intuitively predict that this will negatively impact test performance. A better way to configure a connection for multiple tests is establish it in the Constructor event of the TestSuite class, before the calls to initialize your tests. You can even use the SQLCA global variable. This will make a single connection available to all the tests in the suite. One important note: you cannot code the Disconnect after the initialize calls in the Constructor event. The constructor is invoked and completed before the tests are actually run. A Disconnect in the constructor will also be invoked, closing the connection before the tests are run. Therefore you must code your Disconnect in the TestSuite Destructor event (see Figure 8).

Figure 8: TestSuite with Database Connection

Conclusion
I hope the articles in this series have exposed you to the advantages and possibilities of improving your code quality via unit testing. I also hope they have whetted your appetite for including pbUnit or some other community or vendor-based framework in your tool arsenal. Tools like pbUnit can only grow and become accepted through community participation in their development. If you like pbUnit and can extend its functionality, please consider sharing your extensions. Of late the PowerBuilder community has been quiet, relying more on vendor-provided solutions than community-evolved ones. It is my hope that as we move more toward .NET integration, this trend will change and the community will once again become more active.

Recommended Additional Reading
These books, although not PowerBuilder centric will provide you with valuable insights into TDD and working with legacy code

  • Kent Beck, Test-Driven Development by Example published by Addison-Wesley
  • Michael Feathers, Working Effectively with Legacy Code, Prentice Hall
  • Martin Fowler, Refactoring: Improving the Design of Existing Code, Addison-Wesley
  • Tahchiev, Leme, Massol, and Gregory, JUnit in Action, Manning Publications Company

More Stories By Yakov Werde

Yakov Werde, a 25 year IT industry veteran, is a member of TeamSybase and the newly formed Sybase Customer Evangelist Team. Yakov is a recognized author, speaker and trainer who has been designing and delivering PowerBuilder, .NET, EaServer, Web App Development, and Java training for over 14 years to corporate, military and government developers. Prior to discovering his aptitude as an educator, Yakov worked as an architect, project manager and application coder in the trenches of application software development. Yakov holds a Masters in Education with a specialty in instructional design for online learning from Capella University and a BS in math and computer science from Florida International University. Yakov, managing partner of eLearnIT LLC (www.elearnitonline.com), authors and delivers workshops and web based eLearning tutorials to guide professional developers toward PowerBuilder Classic and .NET mastery. Follow Yakov on Twitter as @eLearnPB

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.