Getting Started: Testing Java Swing GUI with AssertJ Swing

When developing a Java desktop application with Swing, I was faced with the need to directly test the interface, and not just the base classes of controllers / models using unit tests.

This answer (on the topic β€œWhat is the best testing tool for Swing applications?”) Suggested using FEST , which, unfortunately, has been discontinued. However, there are several projects that continued from where FEST remained. One of them (mentioned in this answer ) caught my attention since I used it before in unit tests: AssertJ .

There seems to be AssertJ Swing , which is based on FEST and offers some easy ways to write Swing UI tests. Nevertheless, the transition to the initial / operational setting is cumbersome, since it is difficult to say where to start.




How to create a minimal test setup for the following UI example consisting of only two classes?

Limitations: Java SE, Swing UI, Maven Project, JUnit

public class MainApp { /** * Run me, to use the app yourself. * * @param args ignored */ public static void main(String[] args) { MainApp.showWindow().setSize(600, 600); } /** * Internal standard method to initialize the view, returning the main JFrame (also to be used in automated tests). * * @return initialized JFrame instance */ public static MainWindow showWindow() { MainWindow mainWindow = new MainWindow(); mainWindow.setVisible(true); return mainWindow; } } 



 public class MainWindow extends JFrame { public MainWindow() { super("MainWindow"); this.setContentPane(this.createContentPane()); } private JPanel createContentPane() { JTextArea centerArea = new JTextArea(); centerArea.setName("Center-Area"); centerArea.setEditable(false); JButton northButton = this.createButton("North", centerArea); JButton southButton = this.createButton("South", centerArea); JPanel contentPane = new JPanel(new BorderLayout()); contentPane.add(centerArea); contentPane.add(northButton, BorderLayout.NORTH); contentPane.add(southButton, BorderLayout.SOUTH); return contentPane; } private JButton createButton(final String text, final JTextArea centerArea) { JButton button = new JButton(text); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { centerArea.setText(centerArea.getText() + text + ", "); } }); return button; } } 



I know that the question itself is very broad, so I myself give the answer - to show this specific example.

+1
java swing ui-testing assertj
Oct 10 '17 at 22:01
source share
1 answer

TL; DR: An example project can be found on GitHub .




Assuming this is a maven project, you first need to add at least two dependencies:

  • Unit test structure (e.g. junit here - but can also use testng )
  • The corresponding AssertJ Swing library (e.g. here assertj-swing-junit )

It might look like this (in pom.xml :

 <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-swing-junit</artifactId> <version>1.2.0</version> <scope>test</scope> </dependency> 

Secondly, I usually go for one base test class to separate most of the test setup from the actual tests:

 /** * Base class for all my UI tests taking care of the basic setup. */ public class AbstractUiTest extends AssertJSwingTestCaseTemplate { /** * The main entry point for any tests: the wrapped MainWindow. */ protected FrameFixture frame; /** * Installs a {@link FailOnThreadViolationRepaintManager} to catch violations of Swing threading rules. */ @BeforeClass public static final void setUpOnce() { // avoid UI test execution in a headless environment (eg when building in CI environment like Jenkins or TravisCI) Assume.assumeFalse("Automated UI Test cannot be executed in headless environment", GraphicsEnvironment.isHeadless()); FailOnThreadViolationRepaintManager.install(); } /** * Sets up this test fixture, starting from creation of a new <code>{@link Robot}</code>. * * @see #setUpRobot() * @see #onSetUp() */ @Before public final void setUp() { // call provided AssertJSwingTestCaseTemplate.setUpRobot() this.setUpRobot(); // initialize the graphical user interface MainWindow mainWindow = GuiActionRunner.execute(new GuiQuery<MainWindow>() { @Override protected MainWindow executeInEDT() throws Exception { return MainApp.showWindow(); } }); this.frame = new FrameFixture(this.robot(), mainWindow); this.frame.show(); this.frame.resizeTo(new Dimension(600, 600)); onSetUp(); } /** * Subclasses that need to set up their own test fixtures in this method. Called as <strong>last action</strong> during {@link #setUp()}. */ protected void onSetUp() { // default: everything is already set up } /***************************************************************************************** * Here you could insert further helper methods, eg frequently used component matchers * *****************************************************************************************/ /** * Cleans up any resources used in this test. After calling <code>{@link #onTearDown()}</code>, this method cleans up resources used by this * test <code>{@link Robot}</code>. * * @see #cleanUp() * @see #onTearDown() */ @After public final void tearDown() { try { onTearDown(); this.frame = null; } finally { cleanUp(); } } /** * Subclasses that need to clean up resources can do so in this method. Called as <strong>first action</strong> during {@link #tearDown()}. */ protected void onTearDown() { // default: nothing more to tear down } } 

The actual test class might look like this:

 public class MainWindowTest extends AbstractUiTest { private JButtonFixture northButtonFixture; private JButtonFixture southButtonFixture; @Override protected void onSetUp() { this.northButtonFixture = this.frame.button(JButtonMatcher.withText("North")); this.southButtonFixture = this.frame.button(JButtonMatcher.withText("South")); } @Test public void testWithDifferingComponentMatchers() { // use JTextComponentMatcher.any() as there is only one text input this.frame.textBox(JTextComponentMatcher.any()).requireVisible().requireEnabled().requireNotEditable().requireEmpty(); this.northButtonFixture.requireVisible().requireEnabled().click(); // use value assigned in MainWindow class via JTextArea.setName("Center-Area") to identify component here this.frame.textBox("Center-Area").requireText("North, "); this.southButtonFixture.requireVisible().requireEnabled().click(); // write our own matcher JTextComponentFixture centerArea = this.frame.textBox(new GenericTypeMatcher(JTextArea.class, true) { @Override protected boolean isMatching(Component component) { return true; } }); centerArea.requireVisible().requireEnabled().requireText("North, South, "); } @Override protected void onTearDown() { this.northButtonFixture = null; this.southButtonFixture = null; } } 



Once you have such a basic setup in your project, you may need to examine the different types of components that are, and perhaps introduce a couple of calls to setName() for your various components that you want to test to make your life a little easier.

+1
Oct 10 '17 at 22:01
source share



All Articles