This blog is mainly about Java...

Thursday, July 19, 2012

Unit testing with JOptionPane

If you are like me, you would still like to be able to unit test your swing applications.

However, its difficult to do this when you have a JOptionPane.showConfirmationDialog, and the user needs to type in yes or no.

In this blog post I will show you how you can accomplish this without needing the user to add anything, or changing your domain code too much.


Lets say you have a simple JFrame you want to test that contains a JOptionPane.


public class SimpleFrame extends JFrame {

  public boolean simpleMethod() {
    int showConfirmDialog = JOptionPane.showConfirmDialog(this, "Can we write test for this?", "Question", JOptionPane.YES_NO_OPTION);
    return showConfirmDialog == JOptionPane.YES_OPTION;
  }
}


Now if you create a JUnit test for this, and run it, you will get a JOptionPane and you need to press the YES or NO button.

To avoid this we can change the code to use an interface and then we can create a mock OptionPane for you tests.



/*
* Note you can add all the methods you use in your application
*/
public interface OptionPane {

      /**
       *  @see JOptionPane#showConfirmDialog(Component, Object, String, int, int);
       */
      int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType);
}


Create three implementations of this interface. One that is delegating to JOptionPane, and the others that will be our mock. One of the mocks will return yes, the other no.


public class DefaultOptionPane implements OptionPane {

      public int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType) {
         return JOptionPane.showConfirmDialog(parentComponent,message,title,optionType,messageType);
      }
}

public class YesMockOptionPane extends MockOptionPane {

        @Override
 public int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType) {
   return JOptionPane.YES_OPTION;
 }
}


public class NoMockOptionPane extends MockOptionPane {

        @Override
 public int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType) {
   return JOptionPane.NO_OPTION;
 }
}



Now change your application and add the OptionPane


public class SimpleFrame extends JFrame {
  private OptionPane optionPane = DefaultOptionPane();
  
  public boolean simpleMethod() {
    int showConfirmDialog = optionPane.showConfirmDialog(this, "Can we write test for this?", "Question", JOptionPane.YES_NO_OPTION);
    return showConfirmDialog == JOptionPane.YES_OPTION;
  }

  public void setOptionPane(OptionPane o) { this.optionPane = o; }
}


Now in your tests you use the appropriate MockOptionPane.



@Test
public void test() throws Exception {
  SimpleFrame s = new SimpleFrame()
  s.setOptionPane(new YesMockOptionPane());
  Assert.assertTrue(s.simpleMethod());
}

Thursday, June 7, 2012

Always use unique passwords!

Recently we found out that LinkedIn has managed to loose our passwords, so that they encourage people to create new passwords.

And if you are like many others, you have the same password for other sites. With a very easy trick you can guarantee a unique password for all your sites, and its quite easy.
All you have to do is remember an easy formula which you decide.

The formula is:
followed by the first three (or last three) letters in the url. You can combine this to put it before or after your password.

So for LinkedIn for instance it would be: lin or din or lin or din

By doing this, you don't have to login to all the other sites and change your password if your password ever gets compromised.

Tuesday, May 15, 2012

Add custom Font to your Java Swing application

This task was not that trivial as one might think.

It seems that you need to manually set the Font for each of the UI types.
To find out which one that was supported in my System, I did the following:


java.util.Enumeration<Object> keys = UIManager.getDefaults().keys();
        while (keys.hasMoreElements()) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof FontUIResource) {
                System.out.println(key.toString());
            }
        }

Which printed out the following:


OptionPane.buttonFont
List.font
TableHeader.font
Panel.font
TextArea.font
ToggleButton.font
ComboBox.font
ScrollPane.font
Spinner.font
RadioButtonMenuItem.font
Slider.font
EditorPane.font
OptionPane.font
ToolBar.font
Tree.font
CheckBoxMenuItem.font
TitledBorder.font
FileChooser.listFont
Table.font
MenuBar.font
PopupMenu.font
Label.font
MenuItem.font
MenuItem.acceleratorFont
TextField.font
TextPane.font
CheckBox.font
ProgressBar.font
FormattedTextField.font
CheckBoxMenuItem.acceleratorFont
Menu.acceleratorFont
ColorChooser.font
Menu.font
PasswordField.font
InternalFrame.titleFont
OptionPane.messageFont
RadioButtonMenuItem.acceleratorFont
Viewport.font
TabbedPane.font
RadioButton.font
ToolTip.font
Button.font


Next, I had to set each one of these values manually:

 final Font TAHOMA_PLAIN_11 = new Font("Tahoma", Font.PLAIN, 11);
 final Font MONOSPACED_PLAIN_13 = new Font("Monospaced", Font.PLAIN, 13);
 final Font SEGOE_UI_PLAIN_12 = new Font("Segoe UI", Font.PLAIN, 12);
 final Font DIALOG_PLAIN_12 = new Font("Dialog", Font.PLAIN, 12);


        UIManager.put("OptionPane.buttonFont", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("List.font", TAHOMA_PLAIN_11);
        UIManager.put("TableHeader.font", TAHOMA_PLAIN_11);
        UIManager.put("Panel.font", TAHOMA_PLAIN_11);
        UIManager.put("TextArea.font", MONOSPACED_PLAIN_13);
        UIManager.put("ToggleButton.font", TAHOMA_PLAIN_11);
        UIManager.put("ComboBox.font", TAHOMA_PLAIN_11);
        UIManager.put("ScrollPane.font", TAHOMA_PLAIN_11);
        UIManager.put("Spinner.font", TAHOMA_PLAIN_11);
        UIManager.put("RadioButtonMenuItem.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("Slider.font", TAHOMA_PLAIN_11);
        UIManager.put("EditorPane.font", TAHOMA_PLAIN_11);
        UIManager.put("OptionPane.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("ToolBar.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("Tree.font", TAHOMA_PLAIN_11);
        UIManager.put("CheckBoxMenuItem.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("TitledBorder.font", TAHOMA_PLAIN_11);
        UIManager.put("FileChooser.listFont", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("Table.font", TAHOMA_PLAIN_11);
        UIManager.put("MenuBar.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("PopupMenu.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("Label.font", TAHOMA_PLAIN_11);
        UIManager.put("MenuItem.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("MenuItem.acceleratorFont", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("TextField.font", TAHOMA_PLAIN_11);
        UIManager.put("TextPane.font", TAHOMA_PLAIN_11);
        UIManager.put("CheckBox.font", TAHOMA_PLAIN_11);
        UIManager.put("ProgressBar.font", TAHOMA_PLAIN_11);
        UIManager.put("FormattedTextField.font", TAHOMA_PLAIN_11);
        UIManager.put("CheckBoxMenuItem.acceleratorFont", SwingUtils.DIALOG_PLAIN_12);
        UIManager.put("Menu.acceleratorFont", SwingUtils.DIALOG_PLAIN_12);
        UIManager.put("ColorChooser.font", SwingUtils.DIALOG_PLAIN_12);
        UIManager.put("Menu.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("PasswordField.font", TAHOMA_PLAIN_11);
        UIManager.put("InternalFrame.titleFont", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("OptionPane.messageFont", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("RadioButtonMenuItem.acceleratorFont", SwingUtils.DIALOG_PLAIN_12);
        UIManager.put("Viewport.font", TAHOMA_PLAIN_11);
        UIManager.put("TabbedPane.font", TAHOMA_PLAIN_11);
        UIManager.put("RadioButton.font", TAHOMA_PLAIN_11);
        UIManager.put("ToolTip.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("Button.font", TAHOMA_PLAIN_11);

Running these different JUnit tests proved it worked:

@Test
    public void testFindJavaDefaultFonts() throws Exception {
        java.util.Enumeration<Object> keys = UIManager.getDefaults().keys();
        while (keys.hasMoreElements()) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof FontUIResource) {
                Assert.assertEquals("Dialog", ((Font)value).getFamily());
            }
        }
    }
    
    @Test
    public void testFindSystemDefaultFonts() throws Exception {
        final Font font = new Font("Arial", Font.PLAIN, 12);
        UIManager.put("TextField.font", font);
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        UIManager.put("List.font", font);

        java.util.Enumeration<Object> keys = UIManager.getDefaults().keys();
        while (keys.hasMoreElements()) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof FontUIResource) {
                Assert.assertFalse(key.toString().equals("TextField.font"));
                Assert.assertFalse(key.toString().equals("List.font"));
                
            }
        }
        
        Font font2 = UIManager.getFont("TextField.font");
        Assert.assertNotNull(font2);
        Assert.assertSame(font, font2);

        Font font3 = UIManager.getFont("List.font");
        Assert.assertNotNull(font3);
        Assert.assertSame(font, font3);
    }
    
    @Test
    public void testCustomFonts() throws Exception {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        SwingUtils.updateDefaultFonts(); //Where I put all my UIManager.put(...) lines
        java.util.Enumeration<Object> keys = UIManager.getDefaults().keys();
        while (keys.hasMoreElements()) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof FontUIResource) {
                //This should fail if it comes here since we have set everything manually
                Assert.fail();
            }
        }
        
        Font font = UIManager.getFont("List.font");
        Assert.assertNotNull(font);
        Assert.assertEquals(font.getFamily(), "Tahoma");
    }

If there is an easier way to do this, please do share!

Wednesday, March 28, 2012

WSDL is not part of this compilation. Is this a mistake for wsdl file?

I tried generating a web service client based on wsconsume (which is located in the java distribution folder (bin).

However, upon running the following command:

wsimport.exe -verbose FooBar.wsdl -b Foobar Foobar-jaxb-mapping.xml -s .

I got the following error message:

[ERROR] "file:/C:/workspace/tmp/wsdl/FoobarProxy.wsdl" is not a part of
this compilation. Is this a mistake for "file:/C:/workspace/tmp/FoobarPr
oxy.wsdl"?
line 5 of file:/C:/workspace/tmp/FrontenServiceProxy-jaxb-mapping.xml


I tried googling the error message, and most people suggested that the url had to be changed, and you needed to put #types?schema1 at the end of the url.
However, this wasn't what was wrong for me.

In line 5 of the mapping file, I am referring to the WSDL file. The problem was that in the WSDL file it said that the file should be located in the wsdl folder, and it wasn't. So I created a wsdl folder and put the wsdl file in the correct folder, and voila.

So next time, instead of just blindly googling the error message, I should have read more carefully and looked at the hint "line 5 in the mapping file".
Then I am sure I would have spotted the error!

Lesson learned...

Labels