Name

DEV-06: Set up and use formal unit testing procedures.

Synopsis

A unit test is a test a developer creates to ensure that his or her “unit,” usually a single program, works properly. A unit test is very different from a system or functional test; these latter types of tests are oriented to application features or overall testing of the system. You can’t properly or effectively perform a system test until you know that the individual programs behave as expected.

So, of course, you would therefore expect that programmers do lots of unit testing and have a correspondingly high level of confidence in their programs. Ah, if only that were the case! The reality is that programmers generally perform an inadequate number of inadequate tests and figure that if the users don’t find a bug, there is no bug. Why does this happen? Let me count the ways:

The psychology of success and failure

We are so focused on getting our code to work correctly that we generally shy away from bad news, from even wanting to take the chance of getting bad news. Better to do some cursory testing, confirm that it seems to be working OK, and then wait for others to find bugs, if there are any (as if there were any doubt).

Deadline pressures

Hey, it’s Internet time! Time to market determines all. We need everything yesterday, so let’s be just like Microsoft and Netscape: release pre-beta software as production and let our users test/suffer through our applications.

Management’s lack of understanding

IT management is notorious for not really understanding the software development process. If we aren’t given the time and authority to write (write, that is, in the broadest sense, including testing, documentation, refinement, etc.) our own code properly, we will always end up with buggy junk that no one wants to admit ownership of.

Overhead of setting up and running tests

If it’s a big deal to write and run tests, they won’t get done. I don’t have time, and there is always something else to work on. One consequence of this point is that more and more of the testing is handed over to the QA department, if there is one. That transfer of responsibility is, on the one hand, positive. Professional quality assurance professionals can have a tremendous impact on application quality. Yet we developers must take and exercise responsibility for unit testing our own code, otherwise, the testing/QA process is much more frustrating and extended.

Ego

I wrote it; therefore it works the way I intended it to work.

The bottom line is that our code almost universally needs more testing. And the best way to do unit testing is with a formal procedure built around software that makes testing as easy and as automated as possible. I can’t help with deadline pressures, and my ability to improve your manager’s understanding of the need to take more time to test is limited. I can, on the other hand, offer you a “framework”—a set of processes and code elements—that can greatly improve your ability to perform high quality unit testing.

In the spring of 2000, I studied Extreme Programming (http://www.xprogramming.com) and its associated concepts on unit testing (most widely used in its Java interpretation, the open source JUnit). I then adapted these ideas to the world of PL/SQL, creating utPLSQL, the unit testing framework for Oracle PL/SQL.

By the time this book is published, there may be other unit testing facilities available for PL/SQL. As a starting point for exploring the implementation of formal unit tests for your code, however, I encourage you to visit http://oracle.oreilly.com/utplsql.

Example

With utPLSQL, you build a test package for your standalone or packaged programs. You then ask utPLSQL to run the tests in your test package, and display the results. When you use utPLSQL, you don’t have to analyze the results and determine whether your tests succeeded or failed; the utility automatically figures that out for you.

Suppose, for example, that I have built a simple alternative to the SUBSTR function called betwnstr: it returns the substring found between the specified start and end locations in the string. Here it is:

CREATE OR REPLACE FUNCTION betwnstr (
   string_in IN VARCHAR2,
   start_in IN INTEGER,
   end_in IN INTEGER
   )
   RETURN VARCHAR2
IS
BEGIN
   RETURN (
      SUBSTR (
         string_in,
         start_in,
         end_in - start_in + 1
         )
     );
END betwnstr;
/

To test this function, I want to pass in a variety of inputs, as shown in this table:

String

Start

End

Expected Result

abcdefg

3 (positive number)

5 (bigger positive number)

cde

abcdefg

NULL

Any

NULL

abcdefg

Any

NULL

NULL

abcdefg

5

2 (end smaller than start

NULL

abcdefg

3

200 (end larger than string length)

cdefg

From this table (which, of course, doesn’t yet cover all the variations needed for a comprehensive test), I build test cases for each entry in my test package’s unit test procedure:

CREATE OR REPLACE PACKAGE BODY ut_betwnstr
IS
   PROCEDURE ut_betwnstr IS
   BEGIN
      utassert.eq ('Typical valid usage',
         betwnstr (string_in => 'abcdefg', start_in => 3, end_in => 5),
         'cde'),

      utassert.isnull ('NULL start',
         betwnstr (string_in=> 'abcdefg',
            start_in    => NULL,
            end_in      => 5));

      utassert.isnull ('NULL end',
         betwnstr (string_in=> 'abcdefg',
            start_in    => 2,
            end_in      => NULL));

      utassert.isnull ('End smaller than start',
         betwnstr (string_in => 'abcdefg', start_in => 5, end_in => 2));

      utassert.eq ('End larger than string length',
         betwnstr (string_in=> 'abcdefg',
            start_in    => 3,
            end_in      => 200),
         'cdefg'),

   END ut_betwnstr;
END ut_betwnstr;
/

I call the utAssert procedures so that the results of my tests (my “assertions” that such and such is true) can be logged automatically with utPLSQL.

Then I can run the test and view the results. Here is a run that identifies no errors:

SQL> exec utplsql.test ('betwnstr')
.
>    SSSS   U     U   CCC     CCC   EEEEEEE   SSSS     SSSS
>   S    S  U     U  C   C   C   C  E        S    S   S    S
>  S        U     U C     C C     C E       S        S
>   S       U     U C       C       E        S        S
>    SSSS   U     U C       C       EEEE      SSSS     SSSS
>        S  U     U C       C       E             S        S
>         S U     U C     C C     C E              S        S
>   S    S   U   U   C   C   C   C  E        S    S   S    S
>    SSSS     UUU     CCC     CCC   EEEEEEE   SSSS     SSSS
.
 SUCCESS: "betwnstr"

And here is the output shown when problems arise:

SQL> exec utplsql.test ('betwnstr')
.
>  FFFFFFF   AA     III  L      U     U RRRRR   EEEEEEE
>  F        A  A     I   L      U     U R    R  E
>  F       A    A    I   L      U     U R     R E
>  F      A      A   I   L      U     U R     R E
>  FFFF   A      A   I   L      U     U RRRRRR  EEEE
>  F      AAAAAAAA   I   L      U     U R   R   E
>  F      A      A   I   L      U     U R    R  E
>  F      A      A   I   L       U   U  R     R E
>  F      A      A  III  LLLLLLL  UUU   R     R EEEEEEE
.
 FAILURE: "betwnstr"
.
UT_BETWNSTR: Typical valid usage; expected "cde", got "cd"
UT_BETWNSTR: IS NULL: NULL start
UT_BETWNSTR: IS NULL: End smaller than start

Benefits

You develop applications faster, with a higher degree of confidence and with fewer bugs.

It is much easier for other developers to maintain and enhance your code, because after they make a change, they can run the full suite of tests and confirm that the program still passes all tests.

Challenges

The only challenge to performing comprehensive unit testing is you! You know you have to test your code, and you have to test it repeatedly. So take the time to define your tests within a test package, and use a testing facility to run your tests for you.

Enlist the help of other developers in your organization to review your unit test cases and build others. Did you miss anything? Is your test accurate? It is often difficult for the person who wrote (or is about to write) the code to be objective about it. You’ll find more about this topic in [DEV-07: Get independent testers for functional sign-off.].

Resources

  1. http://oracle.oreilly.com/utplsql : To download utPLSQL and to obtain more information about its approach to unit testing.

  2. http://www.xprogramming.com : For more general information about Extreme Programming’s approach to and underlying principles for unit testing.

  3. http://www.extremeprogramming.org : For a wonderfully accessible, web-based introduction to Extreme Programming.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset