Towards Bug-Free Code

Towards Bug-Free Code

Negative Tests on ProjectX

We set the mock MessagePublisher to throw an Exception after successfully publishing two Messages and then succeed from the fourth Message onwards. So the third row must be retained on the database if the PublishPurchaseOrder.publishNewPOs() is written to handle exceptions correctly.



public class PublishTestCase2 extends TestCase

{

      public void testPublishErrorWithoutPublisherWithMockTable()

                throws SQLException,

                InsufficientCredentialsException

    {

        final Registry registry =

                Registry.getInstance();



        final int publishErrorAt = 3;

        final int undoDeleteforRows = 1;



        //Here we use a DummyPOTable to simulate

        //a Database. But we would've used a mock

        //CachedRowSet even if there were a

        //Database since the idea here is to check

        //if the POMessage which could not be

        //published will still be retained in the

        //Database by invoking undoDelete() on

        //that row.



        final POTable poTable =

                getMockTableDemoForErrorTest(

                undoDeleteforRows);



        registry.setComponentImpl(POTable.class,

                poTable);



        /*

           Setup Mock MessagePublisher to throw

           Exception at publishErrorAt,

           then resume successfully.

        */



        final MockControl publisherControl =

                MockControl.createControl(

                MessagePublisher.class);



        final MessagePublisher publisher =

                (MessagePublisher)

                publisherControl.getMock();



        try

        {

            publisher.publish(new POMessage());

        }

        catch (PublishException e)

        {

            //This can never happen because we are

            //just recording the calls.

        }



        //Expect to be called "publishErrorAt - 1"

        //number of times before Mock is made to

        //throw an exception.

        publisherControl.setVoidCallable(

                publishErrorAt - 1);



        try

        {

            publisher.publish(new POMessage());

        }

        catch (PublishException e)

        {

            //This can never happen because we are

            //just recording the calls.

        }

        publisherControl.setVoidCallable(1);

        publisherControl.setThrowable(

                new PublishException(

                "Message from Mock - " +

                "Publish error occurred!!"));



        try

        {

            publisher.publish(new POMessage());

        }

        catch (PublishException e)

        {

            //This can never happen because we are

            //just recording the calls.

        }

        //Publish will be called on the remaining

        //Messages.

        publisherControl.setVoidCallable(

        poTable.getNumOfNewPOs() - publishErrorAt);



        publisherControl.replay();



        registry.setComponentImpl(

               MessagePublisher.class, publisher);



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



        final int numOfPOsForDemo =

                poTable.getNumOfNewPOs();



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



        PublishPurchaseOrder publishPurchaseOrder =

                new PublishPurchaseOrder();



        final int published =

                publishPurchaseOrder.publishNewPOs();



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



        assertEquals(

              numOfPOsForDemo - undoDeleteforRows,

              published);

    }

        

The code below uses EasyMock to generate a "canned" CachedRowSet that is configured to have five new POs. It also sets it to expect an undo operation on undoDeleteforRows number of those POs. Then it sets the DummyPOTable to use this CachedRowSet for unit testing.



    private POTable getMockTableDemoForErrorTest(

                final int undoDeleteforRows)

                throws SQLException

    {

        final MockControl rowSetControl =

                MockControl.createNiceControl(

                CachedRowSet.class);



        final int newRowsForDemo = 5;



        final CachedRowSet rowSetForDemo =

                (CachedRowSet)

                rowSetControl.getMock();



        rowSetForDemo.next();

        rowSetControl.setReturnValue(true,

                newRowsForDemo);



        rowSetControl.setReturnValue(false,

                MockControl.ZERO_OR_MORE);



        rowSetForDemo.deleteRow();

        rowSetControl.setVoidCallable(

                newRowsForDemo);



        rowSetForDemo.undoDelete();

        rowSetControl.setVoidCallable(

                undoDeleteforRows);



        rowSetForDemo.acceptChanges();



        rowSetForDemo.close();



        rowSetControl.replay();



        //Since this is a demo, we'll simulate a

        //live Database by just returning these

        //data.



        return new DummyPOTable(newRowsForDemo,

                rowSetForDemo);

    }

}

        

The getMockTableDemoForErrorTest(...) method unveils the true power of EasyMock, by allowing us to create a mock/dummy CachedRowSet using just 11 lines of code. Otherwise, we would have been required to implement a total of around 248 methods in the CachedRowSet interface!

Now that we've designed our program carefully, you will also find its modular design invaluable when it comes to replicating or reproducing errors. Error conditions or production configurations can be easily setup to study and fix bugs quickly. Figures 1 and 2 show that the design is indeed modular, with implementations clearly separated from and dependent on abstract, stable classes in separate packages

Figure 1.
Figure 1. Dependencies inside of ProjectX are acyclic

Figure 2.
Figure 2. ProjectX's dependence on the common package is acyclic, and common is stable as it is made up mostly of abstract classes and interfaces

Since we are on the topic of writing bug-free code, there's another place where Java 1.5 features can prove to be very helpful. If you've worked on projects that required internationalization (I18N), then you must be familiar with ResourceBundles.

The most common way of creating locale-specific bundles is by using property files. But the problem with having message keys and values in the property files is that the code that refers to those messages uses plain strings. It is very possible to have spelling mistakes in either the code using the keys or in one of the several locale-specific bundles. Even if a key has been renamed in the bundle, the code can still continue to refer to the old key, since it is just a string and will compile successfully. There's no way of knowing if there's such a mistake until the whole project's code is executed and verified. That could be tiresome or just too late in the process.

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