Towards Bug-Free Code

Towards Bug-Free Code

ProjectX: An Example

We will consider a fictitious project called "ProjectX." Let's assume that there are just two very simple use cases:

  1. Add a purchase order to a database.
  2. Retrieve new purchase orders from the database and publish them to a messaging system for processing.

Obviously, there are two main modules here: the messaging subsystem and the database access subsystem.

Let's say that the developer who is supposed to develop the messaging module falls sick and goes on leave for two weeks, leaving the module half-done. The only things that the developer implementing the second use case has are the database access module and the interfaces to both subsystems that were designed when the team worked together in the first few weeks of the project.

The crucial part of this implementation cycle is to continue with development and testing without the messaging module. This can be accomplished by providing a dummy implementation of the messaging interfaces. And the easiest way is to replace the proposed implementation with the dummy.



public class UseCase2

{

    public void retrieveAndPublishPOs()

    {

        MessagePublisher messagePublisher =

                new DummyMessagePublisher();



        ....



        /*

         Retrieve POs from Database

         and publish each one.

        */



        messagePublisher.publish(po);



        ....

    }

}

        

When the messaging module is ready, DummyMessagePublisher can be replaced by the actual implementation. However, you'll notice that even though we have taken the trouble of extracting an interface for the MessagePublisher, we've still not been able to break free from its implementation because the new DummyMessagePublisher() will have to be replaced each time by whatever implementation we come up with. Which brings us back to square one. This is where providing "hooks" into the system allows for a lot of flexibility. Consider how easy the whole thing would have been, had we used:



MessagePublisher messagePublisher =

                Registry.getImplFor(

                MessagePublisher.class)



//And set the "impl" using this

registry.setComponentImpl(MessagePublisher.class,

                new DummyPublisher())



//or this in the setUp() method.

registry.setComponentImpl(MessagePublisher.class,

                new MyJMSPublisher())

        

Unit-testing ProjectX

Now that we have made accommodations for introducing any implementation for the interfaces, we'll see how easy it is to unit test this use case even without the messaging subsystem.



import common.registry.Registry;

import junit.framework.TestCase;

import org.easymock.MockControl;

import projectx.data.POMessage;

import projectx.database.POTable;

...

import projectx.messaging.MessagePublisher;

...

import projectx.usecases.usecase2.

                PublishPurchaseOrder;



public class PublishTestCase extends TestCase

{

    @Override protected void setUp()

                throws Exception

    {

        final Registry registry =

                Registry.getInstance();



        //--- setup OraclePOTable ---



        final OraclePOTable oraclePOTable =

                new OraclePOTable();



        registry.setComponentImpl(POTable.class,

                oraclePOTable);



        //--- setup Mock MessagePublisher ---



        final MockControl publisherControl =

                MockControl.createControl(

                MessagePublisher.class);



        final MessagePublisher publisher =

                (MessagePublisher)

                publisherControl.getMock();



        //Record the methods that will be invoked.

        try

        {

            publisher.publish(new POMessage());

        }

        catch (PublishException e)

        {

            //This can never happen because we are

            //just recording the calls.

        }



        //Expect to be called getNumOfNewPOs()

        //number of times.

        publisherControl.setVoidCallable(

                oraclePOTable.getNumOfNewPOs());



        //Set the MockControl to replay the

        //recorded methods.

        publisherControl.replay();



        registry.setComponentImpl(

                MessagePublisher.class,

                publisher);

    }



 public void testSuccesslPublishWithoutPublisher()

                throws SQLException,

                InsufficientCredentialsException

    {

        final POTable poTable =

                Registry.getImplFor(POTable.class);



        final int numOfPOsForDemo =

                poTable.getNumOfNewPOs();



        //---------------------------------------



        PublishPurchaseOrder publishPurchaseOrder

                = new PublishPurchaseOrder();



        final int published =

                publishPurchaseOrder.publishNewPOs();



        //---------------------------------------



        assertEquals(numOfPOsForDemo, published);

    }

}

        

If you read the setUp() method of this JUnit test case, you will see that it requires just four lines of code using the EasyMock library to place a mock implementation of MessagePublisher and set it up to expect the publish() invocation a certain number of times. The testSuccessfulPublishWithoutPublisher() test makes note of the number of new POs that have to be published using poTable.getNumOfNewPOs() before running the test. Then, it runs the actual test of invoking publishPurchaseOrder.publishNewPOs(), which returns the number of POs that were retrieved from the database and successfully published to the MessagePublisher.

The PublishPurchaseOrder's publishNewPOs() method looks like this:



public int publishNewPOs() throws SQLException,

                InsufficientCredentialsException

{

    final POTable poTable =

                Registry.getImplFor(POTable.class);



    final MessagePublisher publisher =

                Registry.getImplFor(

                MessagePublisher.class);



    int success = 0;

    final CachedRowSet rowSet =

                poTable.getNewPOs();



    while (rowSet.next())

    {

        POMessage tempMessage = new POMessage();

        /*

         Populate the PO object with the data in

         the current row.

        */

        try

        {

            publisher.publish(tempMessage);

            rowSet.deleteRow();



            success++;

        }

        catch (PublishException e)

        {

            logger.error("Error occurred while " +

                "publishing PO: " +

                tempMessage.toString(), e);



            try

            {

                rowSet.undoDelete();

            }

            catch (SQLException e1)

            {

                logger.error("Undo operation on" +

                deleted PO failed: " +

                tempMessage.toString(),

                e1);

            }

        }

    }



    rowSet.acceptChanges();

    rowSet.close();



    return success;

}

        

Again, publishNewPOs() must rely only on interfaces (MessagePublisher and POTable) to remain useful.



public interface POTable extends Component

{

    /**

     * @param message

     * @return The Order number of this newly

     * created PO.

     * @throws SQLException

     */

    public long addPO(POMessage message)

                throws SQLException,

                InsufficientCredentialsException;



    public int getNumOfNewPOs()

                throws SQLException;



    public CachedRowSet getNewPOs()

                throws SQLException,

                InsufficientCredentialsException;



    public void deletePO(POMessage message)

                throws SQLException,

                InsufficientCredentialsException;

}



public interface MessagePublisher extends Component

{

    public void publish(Message msg)

                throws PublishException;

}

        

After having written this test case, which covers only the "success path," we soon run out of excuses for not writing tests. So the next step would be to write a test case and see how the PublishPurchaseOrder.publishNewPOs() fares when the MessagePublisher throws an Exception in the publish(...) method.

This time, in addition to using a mock MessagePublisher, we will also use a DummyPOTable that uses a mock CachedRowSet to record and finally, verify that error-handling/compensating methods get invoked when a PO does not get published successfully.

Prev  [1] [2] [3] [4] Next

Close    To Top
  • Prev Article-Java:
  • Next Article-Java:
  • Now: Tutorial for Web and Software Design > Java > Java Basic > Java Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction