How to test the JUnit GUI with soup.

Intro.

In this tutorial, we will create a small JUnit test case to test a JUnit GUI. As you could see we will test JUnit GUI and use JUnit to run the tests. So, if you are not pretty comfortable with creating and maintaining JUnit test cases take a look at here .

Requirements.

The requirement is to check that, when a test fail the Exception that caused the failure is show in the GUI. To be more specific we need to:

  • start junit.swingui.TestRunner.
  • type a name of a test that we know will fail.
  • click on the run button.
  • click on the first element on the list of failures.
  • check that the contents of the failure detail match with the expected.
  • A test case that fails

    As mentioned before we will need a test case that fails. We will use MyTest as described in:

    import junit.framework.TestCase;
    public class MyTest extends TestCase {
    	public MyTest(String arg0) {
    		super(arg0);
    	}
    	public void testFail(){
    		// I fail by throwing this exception.
    		throw new UnsupportedOperationException("failure.");
    	}
    }

    Bellow, we can see the JUnit screen after running MyTest.java.

    test result in JUnit

    As you can see, the failure detail has the text:

    And we are going to write a test case to confirm that in the next sessions.

    The JUnitGUITest

    The first thing we need is to know how to open the JUnit GUI. That can be done with this code:

    	testRunner = new TestRunner();
    	testRunner.start(new String[] {});
    

    The next step is to find a way to get access to the TextArea that has the message we want to check. This is not an easy task because there is no public interface to do that. We will have to intercept the TextArea creation to get a reference to it. We can do that with:

    	JTextArea failureDetailTextArea;
    	.
    	.
    	.
    	// The only way to have access to the 
    	// failure detail text area is by intercepting
    	// its creation.
    	testRunner = new TestRunner(){
    		// We sub class the method that creates the TextArea.
    		protected FailureDetailView createFailureDetailView(){
    			// call the method that actually creates the component.
    			FailureDetailView view = super.createFailureDetailView();
    			// we save the reference in a variable accessible 
    			// to our test case.
    			failureDetailTextArea = (JTextArea) view.getComponent();
    			// return the result of the super method.
    			return view;
    			
    		}
    	};
    	.
    	.
    	.
    	testRunner.start(new String[] {});
    

    With an accessible reference to the TextArea we can now write the code that checks its contents after the test execution. I should look like:

    	.
    	.
    	.
    	//Check that the fail message starts with 
    	// the name of the exception thrown is enough. 
    	// for now, we don't need to check the stack. 
    	String expectedFailureMessage = "java.lang.UnsupportedOperationException: failure.";
    	String failMessage = failureDetailTextArea.getText();
    	assertTrue(failMessage.startsWith(expectedFailureMessage));
    	.
    	.
    	.
    

    What is missing now is the interaction with the JUnit GUI and that is where we can use soup's help. We can use soup to record the user interaction we need and generate the Java code so that we can use it in our test case. To record JUnit interaction we need to open JUnit through soup, as in:

    That should open the soup window in the upper left corner of your screen and the JUnit window in the middle of the screen. We are assuming that MyTest.java and soup.jar are in the current directory (.) and that the JUNIT_HOME points to a directory that has the junit.jar file.

    Before we start recording, you should make sure that the "Test class name" field is clean. JUnit keeps a history of the last run tests, which is good for usability but is not so good for our test. So, if that field has any text, just clean it. We will come back to this subject later.

    To start recording, click on the record button record button in the control and start the interaction with JUnit to run MyTest test class:
  • click on the "Test class name" field.
  • Type MyTest in the field.
  • click on Run button.
  • when the test execution finishes, click on the first item in the failures list.
  • That is all the interaction we need for our test. so, we can stop recording by clicking on stop button in the control panel.

    Let's copy the generated Java code so that we can compose the final test case. Click on the event browser button event browser button and a screen like this should open.

    event browser button

    Now, select all the items in the top panel of the event browser window by clicking on the first, scrolling to the last and clicking on the last item while holding the shift button. Click on the copy button copy button and it will copy all the generated source code to the clipboard. If you paste in a text editor it should look like this:

    	robotHome.getRobot().mouseMove(241,289);
    	robotHome.getRobot().mousePress(16);
    	robotHome.getRobot().mouseRelease(16);
    	robotHome.getRobot().keyPress(16);
    	robotHome.getRobot().keyPress(77);
    	robotHome.getRobot().keyRelease(77);
    	robotHome.getRobot().keyRelease(16);
    	robotHome.getRobot().keyPress(89);
    	robotHome.getRobot().keyRelease(89);
    	robotHome.getRobot().keyPress(16);
    	robotHome.getRobot().keyPress(84);
    	robotHome.getRobot().keyRelease(84);
    	robotHome.getRobot().keyRelease(16);
    	robotHome.getRobot().keyPress(69);
    	robotHome.getRobot().keyRelease(69);
    	robotHome.getRobot().keyPress(83);
    	robotHome.getRobot().keyRelease(83);
    	robotHome.getRobot().keyPress(84);
    	robotHome.getRobot().keyRelease(84);
    	robotHome.getRobot().mouseMove(646,285);
    	robotHome.getRobot().mousePress(16);
    	robotHome.getRobot().mouseRelease(16);
    	robotHome.getRobot().mouseMove(436,422);
    	robotHome.getRobot().mousePress(16);
    	robotHome.getRobot().mouseRelease(16);
    	robotHome.getRobot().mouseMove(217,231);
    
    

    With this we can compose the final test case that should look like:

    import java.io.File;
    import javax.swing.JTextArea;
    import junit.framework.Test;
    import junit.framework.TestCase;
    import junit.framework.TestSuite;
    import junit.runner.FailureDetailView;
    import junit.swingui.TestRunner;
    
    import soup.framework.RobotHome;
    
    
    public class JUnitGUITest extends TestCase {
    	RobotHome robotHome;
    	JTextArea failureDetailTextArea;
    	TestRunner testRunner;
    
    	public JUnitGUITest(String arg0) {
    		super(arg0);
    	}
    
    	protected void setUp() throws Exception {
    		super.setUp();
    		//Initialize the variable used by the generated test code.
    		robotHome = RobotHome.getInstance();
    
    		// JUnit keeps a history of the test ran in a file.
    		// It fills the test class name field with the
    		// last run test. This could make this test case
    		// execution nonderteministic, which is highly 
    		// inapropriate for our tests.
    		String home= System.getProperty
    		String home= System.getProperty("user.home");
     		File historyFile = new File(home,".junitsession");
     		historyFile.delete();
    
    		// The only way to have access to the 
    		// failure detail text area is by intercepting
    		// its creation.
    		testRunner = new TestRunner(){
    			// We sub class the method that creates the TextArea.
    			protected FailureDetailView createFailureDetailView(){
    				// call the method that actually creates the component.
    				FailureDetailView view = super.createFailureDetailView();
    				// we save the reference in a variable accessible 
    				// to our test case.
    				failureDetailTextArea = (JTextArea) view.getComponent();
    				// return the result of the super method.
    				return view;
    				
    			}
    		};
    		
    		// Number 1 in the requirements list, 
    		// here I show the screen. 
    		testRunner.start(new String[] {});
    	}
    
    	protected void tearDown() throws Exception {
    		super.tearDown();
    		testRunner = null;
    		robotHome = null;
    	}
    	public void testJUnitGUI(){
    		// Begin of source generated by soup
    		robotHome.getRobot().mouseMove(241,289);
    		robotHome.getRobot().mousePress(16);
    		robotHome.getRobot().mouseRelease(16);
    		robotHome.getRobot().keyPress(16);
    		robotHome.getRobot().keyPress(77);
    		robotHome.getRobot().keyRelease(77);
    		robotHome.getRobot().keyRelease(16);
    		robotHome.getRobot().keyPress(89);
    		robotHome.getRobot().keyRelease(89);
    		robotHome.getRobot().keyPress(16);
    		robotHome.getRobot().keyPress(84);
    		robotHome.getRobot().keyRelease(84);
    		robotHome.getRobot().keyRelease(16);
    		robotHome.getRobot().keyPress(69);
    		robotHome.getRobot().keyRelease(69);
    		robotHome.getRobot().keyPress(83);
    		robotHome.getRobot().keyRelease(83);
    		robotHome.getRobot().keyPress(84);
    		robotHome.getRobot().keyRelease(84);
    		robotHome.getRobot().mouseMove(646,285);
    		robotHome.getRobot().mousePress(16);
    		robotHome.getRobot().mouseRelease(16);
    		robotHome.getRobot().mouseMove(436,422);
    		robotHome.getRobot().mousePress(16);
    		robotHome.getRobot().mouseRelease(16);
    		robotHome.getRobot().mouseMove(217,231);
    		// End of code generated by soup
    				
    		//Check that the fail message starts with 
    		// the name of the exception thrown is enough. 
    		// for now, we don't need to check the stack. 
    		String expectedFailureMessage = "java.lang.UnsupportedOperationException: failure.";
    		String failMessage = failureDetailTextArea.getText();
    		assertTrue(failMessage.startsWith(expectedFailureMessage));
    
    
    	}
        public static void main(String[] args) {
            junit.textui.TestRunner.run(suite());
        }
        public static Test suite() {
            return new TestSuite(JUnitGUITest.class);
        }
    }
    

    There are some things in the test case that we haven't talked yet. There is a RobotHome variable that is part of the soup framework and it is used by the generated code. We initialize the variable at the setup method.

    JUnit keeps a history of the test ran in a file. It fills the test class name field with the last run test. This could make this test case execution nonderteministic, which is highly inappropriate for our tests. This is done in the setup method as well.

    With the setup method creating all the fixtures that we need, (initializing variable, opening the JUNIT screen, etc. ) we can now go to the test case itself, the testJUnitGUI method. It does two things: use the soup generated code to do the action in the JUnit window and check that the result is as expected.

    You may have noticed that there is a main method in this test case that pass itself to JUnit text based interface. I believe that running the JUnitGUITest class is the last confusing way of running its tests so that you have only the JUNIT window being tested open. So, in order to run the just created test, do:

    assuming that you are in a directory that has soup.jar and JUnitGUITest and that JUNIT_HOME environment variable points to a dir that has junit.jar.

    Source code

    MyTest.java

    JUnitGUITest.java (without the generated soup code.)


    SourceForge.net Logo