LIFT: The Library for InterFace Testing
LIFT was written to make GUI Unit Testing quick and easy. It was written with introductory programming students in mind, but can be used by anyone using Java and Swing to create graphical user interfaces. LIFT does three main things for the programmer:
- Provides a simple, type safe way to retrieve GUI object references.
- Provides a simple means of simulating mouse and keyboard interactions.
- Transparently deals with thread synchronization issues inherent to GUI programming.
In general, testing GUIs using LIFT is a three step process:
- Retrieve references to any components needed for the test using getComponent().
- Simulate user activity by using LIFT methods (click(component), keyString(component, string), etc)
- Make assertions about the program state to ensure that it behaved as expected.
Lift is compatible with with both JUnit 3 and JUnit 4 style tests. For JUnit 4 style tests, JUnit 4.8.2 or later is required. The latest JUnit is availabe at The JUnit Website.
For more information, see the following:
Testing GUI Programs, an introductory web presentation (using Prezi) that explains how to use LIFT.
LIFT: Taking GUI Unit Testing to New Heights, our SIGCSE 2011 conference paper describing this library.
Supporting Student-Written Tests of GUI Programs, a SIGCSE 2008 conference paper describing the objectdraw version of this library.
A Simple Example
A program that lets the user enter a fahrenheit temperature and displays the equivalent celsius temperature after clicking a "Convert" button. A test for this program follows.
1 public void testTempConverter()
2 {
3 //This test will use the text field the user enters the temperature into.
4 //There is only one text field in the program so the type is all we need to give getComponent()
5 JTextField entryField = getComponent(JTextField.class);
6
7 //We also need the label that will store the result.
8 //There are 2 labels in program so we look for the one that starts with the value 0.
9 JLabel result = getComponent(JLabel.class, where.textIs("0.0"));
10
11 //Lastly we'll need a reference to the convert button so we can click it.
12 //Like the entry field, there is only one button so we only need to specify the type.
13 JButton convertButton = getComponent(JButton.class);
14
15 //For the actual test we will convert 212 degrees fahrenheit.
16 //Use a LIFT method to enter the value into the text field.
17 keyString(entryField, "212");
18 //Use a LIFT method to click on the button.
19 click(convertButton);
20
21 //Make sure the result label was updated to 100.0.
22 assertEquals("Conversion incorrect", "100.0", result.getText());
23 }
In this test, the first few lines retrieve the components needed for the test. LIFT methods are then used to simulate a user entering the value 212 and clicking on the convert button. Finally, the result label is inspected to make sure it updated with the correct value. Because the entry field and the button were the only components of those types in the program, getComponent() only needed to know the type to retrieve them. For components that are not the only instance of a particular type of component, more specificity is needed, such as when retrieving the result label in the above example. In this case, we use the textIs() filter to look for a JLabel with the specified text. Components can also be identified using other attributes such as name, location, size, parent (in the UI hierarchy), or status (enabled, focus). These attributes can then be combined using boolean operators to be even more specific.
1 getComponent(JButton.class, where.textIs("Next").and.enabledIs(false))
would return a disabled "Next" button.
In practice, we found that the easiest way for students to test their GUIs was to use the setName() method to give a unique name to any components they knew they would need to reference in a test. Then in their tests they can use the nameIs() filter to quickly retrieve a reference to the component. This technique ended up being so useful we added an overloaded version of getComponent() to take advantage of this. If a student declares a component like this in their implementation:
1 JButton cancel = new JButton("Cancel"); cancel.setName("cancelButton");
they can then retrieve the component like this in a test:
1 JButton c = getComponent(JButton.class, "cancelButton");
The full source for this example: Application Test Class JUnit 4 Test Class
Testing with other Graphics Libraries
LIFT started out as a testing framework for the objectdraw teaching library. It was then adapted for the ACM Java Task Force library, before finally being adapted for use with any Swing application. Legacy support for both objectdraw and ACM JTF remains however, and may be useful in the classroom.
Objectdraw example
This example shows how to create and test a simple program using objectdraw and LIFT.
1 public void begin()
2 {
3 //create two rectangles
4 r = new FilledRect(20, 20, 20, 20, canvas);
5 r.setColor(Color.red);
6 r2 = new FilledRect(60, 60, 40, 40, canvas);
7 r2.setColor(Color.blue);
8 }
9 //a portion of the code to control dragging one of the boxes
10 public void onMouseDrag( Location point )
11 {
12 if(dragging)
13 r.moveTo(point);
14 }
15 ...
16 //And some tests
17 public void testExample()
18 {
19 //Finding an rectangle based on its color
20 FilledRect r = getDrawable(FilledRect.class, where.colorIs(Color.red));
21 assertTrue(r.getHeight() == 20);
22
23 //Drag one of the boxes
24 ode.onMousePress(new Location(25, 25));
25 ode.onMouseDrag(new Location(60, 60));
26 ode.onMouseRelease(new Location(70, 70));
27
28 //Make sure it moved correctly
29 assertTrue(r.getLocation().equals(new Location(70, 70)));
30 }
The full source for this example: Application Test Class JUnit 4 Test Class
ACM JTF Example
The same example, now written using ACM JTF.
1 public void run()
2 {
3 //Create and add two GRects
4 r1 = new GRect(20, 20, 20, 20);
5 r1.setColor(Color.red);
6 add(r1);
7 r2 = new GRect(60, 60, 40, 40);
8 r2.setColor(Color.blue);
9 add(r2);
10 }
11 //a portion of the code to control dragging one of the boxes
12 public void mousePressed(GPoint p)
13 {
14 if(r1.contains(p))
15 dragging = true;
16 }
17 ...
18 //And some tests
19 public void testExample()
20 {
21 //Finding an object based on its color
22 GRect r = getGObject(GRect.class, where.colorIs(Color.red));
23 assertTrue(r.getHeight() == 20);
24
25 //Find an object based on its location
26 GRect r2 = getGObject(GRect.class, where.locationIs(60, 60));
27 assertTrue(r2.getColor() == Color.blue);
28
29 //Drag one of the boxes
30 jtfe.mousePressed(new GPoint(25, 25));
31 jtfe.mouseDragged(new GPoint(60, 60));
32 jtfe.mouseReleased(new GPoint(70, 70));
33
34 //Make sure it moved correctly
35 assertTrue(r.getLocation().equals(new GPoint(70, 70)));
36 }
The full source for this example: Application Test Class JUnit 4 Test Class
Downloading the Library
LIFT is included as part of the "Student Library", a jar file available from our SourceforgeProject:
http://sourceforge.net/projects/web-cat/files/Student%20Library/
LIFT (and the whole student library) is available for use under the terms of the LGPL v3.
The javadoc for the student library is available at http://courses.cs.vt.edu/~cs1114/api.