Tech Articles


Refactoring Classic PowerBuilder Applications Using TDD and pbUnit


The migration march to PB 12.NET will have many shops revisiting legacy applications. In my previous article, "Refactoring Is Not an ‘R' Word" (PBDJ, Vol. 16, issue 12), 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. You were also introduced to pbUnit, an open source tool and framework that you can use for both refactoring and developing new code in PB Classic applications. In this article I'll guide you through installing pbUnit and help you master the basic algorithm when refactoring your PB legacy code with pbUnit and test driven methodology.

Installing pbUnit and Configuring Your App: The Nuts and Bolts
In addition to installing and using pbUnit with Classic PowerBuilder to run unit Tests, you'll also learn how to get your code under test so you can go about refactoring your code with confidence. After that, I'll show you how to do a couple of refactorings to thin out a GUI and partition business and data logic

Now is the time to take that old code and whip it into shape. You suspect that unit test drive development has some merit and would like to try it out. This article will show you how to get started.

There are a couple of ways you can get your hands on pbUnit. You can download a PB v10.2 version from the Sybase CodeXchange. This version will migrate without error to version 10.5, 11.x or 12.0 Classic. Point your browser to http://www.sybase.com/developer/codexchange, then proceed to the PowerBuilder section and find pbUnit. You'll want to take version 3.2. Alternatively, if you want me know that you're trying out TDD, you can drop me an email and I'll send you a zip with a PB 12.0 compatible version. I didn't change the doc since I didn't make any changes to the code. I upped the version number on my zip to 3.3 to indicate the change.

The beautiful thing is that pbUnit is written in PowerBuilder and is provided with source code. It's truly a "native son." You can see how it works, modify it to suit your individual needs, and participate in the community effort to grow the tool by sharing your enhancements with others. You will need to install two pieces to use pbUnit as an automated test harness: the app extension piece and the tester GUI piece. The app piece is housed in a single PBL that contains the ancestor CCUOs that you extend and implement to write xUnit type unit test cases in your app. Just add this PBL to your library list and you're off and running. The GUI piece is a standalone test harness app that allows you to rapidly execute tests outside the PB IDE or within your application. I find running the GUI outside the PB IDE an ideal approach. It is the one I will show in this article. For the sake of completeness, please note that it is possible to embed the pbTest GUI in your application and launch it within your application. It is also possible to run tests without the GUI. However, I will not demonstrate either of those approaches in this article.

Installing pbUnit is a piece of cake. First, create a separate folder off your workspace or target folder and name it pbUnit, then unpack the pbUnit3.3.zip file into it. Figure 1 shows what you'll have when you unzip the archive.

I'm going to start with the GUI by installing the GUI (you'll see why in a few moments).

My archive contains a compiled version of the GUI. Attempt to run the exe provided (I compiled it using 12.0 build 5107 [beta 2]). If it runs, you're done. If it doesn't run, you'll need to migrate the app to your version. To do, add PBUnitGUI.pbt to your version 12 Classic workspace and migrate it by right-clicking on the target and choosing Migrate. I won't elaborate on this as it is standard PowerBuilder operating procedure. Then deploy the app using the project object pr_pbunit (or pr_pbunit32 if your prefer machine code). Click the build button to force the painter to adjust the library paths to match your configuration, adjust the paths for the pbr and exe, make sure that PBDs are selected for every library, and deploy the application. When you run the EXE, it should look like Figure 2.

When you select a target and click OK, you should see what's shown in Figure 3. Okay, you're almost ready to rock and roll.

Copy the base library, PBUnit.pbl, from the pbUnit folder to the folder that contains your application PBLs and add it to the application library list. While you're at it, create a new library named Test_Cases and add it to the end of your library list. Put your test cases into this library. In an enterprise development effort you will have a test PBL for each application PBL whose code you bring under test. That's it; you're now ready to write your first test.

Writing Your First Test
Here's the basic TDD algorithm:

  • Pick a method you want to refactor and examine the code. Because TDD is method-level white box testing, it's crucial to read the code and understand it before writing your tests.
  • Take paper and pencil and write some assertions about what the code does given a known set of inputs. These logic statements assert what the method will return or what changes it will make to the memory or the GUI. You characterize the method behavior in your test.
  • Write a test in PowerScript to exercise the code against your assertions (an example follows).
  • Run the test in pbUnit and watch it fail.
  • Correct the test to make it pass. This will prove that your test works.
  • Repeat the coding process in order to cover your code with several tests that span the universe of possible inputs and outputs.

Once you know your tests work, you can go ahead and refactor your code, running the tests after each change to ensure that refactoring didn't break the code. This may seem like a lot of work, but it's really not. The coding and testing go quite fast once you get the process into your blood.

To write a single or a related set of tests in pbUnit, you inherit from the TestCase CCUO located in the PBUnit PBL. You code each test in a separate custom user event inside the CCUO. There is usually a one-to-one relationship between a TestCase class and a class under test. To conform with naming conventions, start each test name with test_. Put each test in a separate event so that the outcome of one test doesn't affect the results of other tests. pbUnit runs each test event in isolation from any others.

A typical test has of four sections:

  1. Setting up to do the test -you write code to get the state of the system just right so the test can be run.
  2. Executing the code that is under test
  3. Comparing the results of the run against what you expected to determine whether the test succeeded or failed; this is your assertion
  4. Tearing down the setup to clean up memory after the test

To keep things clear, I'm going to illustrate the process using this exceedingly simple interest calculator (see Figure 4).

Figure 5 shows the clicked method (event) of the calc button that we'll get under test and then refactor. Assume this instance variable: constant decimal idc_interest_rate = 4.08

Write a test that characterizes how the system behaves with positive input by inheriting from pbUnit's TestCase class and saving the CCUO as Test_Calculator in your TestCases PBL. Add a custom user event named test_positives. Here's the code for the test:

//(1) Setting up to do the test
// Disregard that Windows are visual objects (you're not testing the GUI here)
w_calctester lw_calctester // create the window in memory so you can get at its members
lw_calctester = Create w_calctester
//(2) Executing the code that is under test
lw_calctester.sle_years.text ='5'                                  //provide inputs
lw_calctester.sle_amount.text = '50000'
lw_calctester.cb_calc.triggerevent(clicked!)   //execute the method
//(3) comparing the results of the run against expected results
// The boolean Assert method has 2 parameters, (1) An error message to display in the GUI if the test fails and (2) The result you expect based on the conditions you provided
this.assert( 'Positive Value Incorrectly Calculated', &
lw_calctester.sle_interest_paid.text ='0' )  //this test will obviously fail! We provided a wrong result!
//(4) Tearing down the setup to clean up after the test.
destroy lw_calctester

Save the test case. Leaving the PB IDE open, navigate to the pbUnit GUI, locate your target, find your test case and run it. You will get the failure message and the red bar because your assertion is wrong. The interest will not be 0 (see Figure 6).

Navigate over to PB and correct your assertion. You are really expecting that the SLE will contain ‘$10,200.00.' Save your change and rerun the test. You will get the Green Bar - congratulations (see Figure 7).

Now you can write a couple more assertions to fully characterize the calculator's behavior. Add two events, one characterizing the behavior with negative value inputs and the other with zero inputs. Follow the algorithm you used above to set up, run the test, make the assertion and tear down.

Once the tests are in place you can confidently go about restructuring and streamlining the code in the clicked event, covered by tests demonstrating that your refactoring did not in any way alter program behavior.

You're now on your way to mastering a powerful methodology for refactoring your code using unit testing. You might already see how refactoring using a Test Driven Development methodology helps you to:

  • Think more deeply and completely about your code
  • Really exercise your code to remove all defects and flaws
  • Refactor with confidence surrounded by a safety net of unit tests

If you'd like to watch me do this demo in action, check out my Refactoring tutorial on Sybase.com at http://video.sybase.com/products/powerbuilder/future-proofing-pb/player.html . The demo is midway through the tutorial.

What's Next?
In my next article I'll show you a technique to roll multiple test cases into a test suite and an approach to writing test cases for database centric methods.

 

--This article was originally published on PBDJ.

 

Comments (0)
There are no comments posted here yet

Find Articles by Tag

Resize Validation SnapDevelop Web API DataWindow JSON SQL Server OAuth 2.0 ODBC Migration Excel Array File Data DLL JSONGenerator SqlModelMapper Windows OS JSON License Authorization Platform UI SqlExecutor Bug Mobile Database Table Web Service Proxy Database OAuth Messagging SVN .NET Std Framework GhostScript PostgreSQL ODBC driver Deployment WebBrowser Import Oracle Database Painter External Functions Windows 10 Icons Database Table Data PowerBuilder Compiler JSONParser DataType Automated Testing 64-bit Branch & Merge Application Azure Source Control Event HTTPClient Database Connection Debugger Event Handler Encryption Icon Debugging Filter Service Export JSON Graph Transaction Installation Linux OS DragDrop UI Themes PDF NativePDF Design TFS Script Android Elevate Conference CrypterObject TLS/SSL Export BLOB Text Performance UI Modernization SDK ActiveX Model SnapObjects Expression API SQL Authentication Interface Testing Debug PostgreSQL RichTextEdit Control .NET DataStore Source Code .NET Assembly PowerScript (PS) Repository MessageBox TortoiseGit XML OLE WinAPI CoderObject Jenkins TreeView Window Git Event Handling Open Source REST Class PowerBuilder Sort OrcaScript 32-bit IDE PFC SOAP DevOps PowerServer Web RibbonBar Builder iOS Outlook Database Profile COM Charts Trial DataWindow Import JSON Variable Syntax Configuration Stored Procedure Encoding RESTClient PBDOM CI/CD RibbonBar Error PowerBuilder (Appeon) Database Object Database Table Schema Menu PDFlib PowerServer Mobile C# InfoMaker