Skip to content

Technik Blog

Programmieren | Arduino | ESP32 | MicroPython | Python | Raspberry Pi | Raspberry Pi Pico

Menu
  • Smarthome
  • Arduino
  • ESP32 & Co.
  • Raspberry Pi & Pico
  • Solo Mining
  • Über mich
  • Deutsch
  • English
Menu

Java Swing GUI Test mit AssertJ

Posted on 2. August 20187. Juni 2024 by Stefan Draeger

Wie man eine JavaFX Anwendung testet habe ich bereits im Tutorial JavaFX: Automatisierte GUI Test mit JUnit und TestFX erläutert. In diesem Tutorial möchte ich nun auf das Testen einer Oracle Java Swing Anwendung eingehen.

  • Testprojekt anlegen
    • Blankes Maven Projekt in Eclipse erzeugen
    • Einfaches Swing Projekt erzeugen
  • AssertJ im Projekt einrichten
  • Ersten JUnit Test anlegen
    • Video
  • Ein komplexes Beispiel
    • Quellcode
    • Erstellen der Testfälle
    • Video
    • Troubleshooting
      • Fehlerhafte Darstellung des Fensters
      • ComponentLookupException: Unable to find component using matcher
  • Download
  • Quellen und Referenzen
  • Fazit

Testprojekt anlegen

Als erstes richte ich ein kleines Oracle Java 8 Projekt mit Apache Maven in der Entwicklungsumgebung Eclipse ein.

Blankes Maven Projekt in Eclipse erzeugen

"Schritt
Schritt 2 - Markiere die Checkbox "Create a simple...."
Schritt 2 – Markiere die Checkbox „Create a simple….“
Schritt 3 - Eingeben der Metadaten zum Projekt
Schritt 3 – Eingeben der Metadaten zum Projekt

Einfaches Swing Projekt erzeugen

Nach dem Eclipse das Maven Projekt mit der typischen Struktur angelegt hat erzeugen wir nun eine kleine GUI, welche wir mit AssertJ testen wollen.

SimpleSwingGui für AssertJ Tests
SimpleSwingGui für AssertJ Tests

Die „Anwendung“ enthält zwei Eingabefelder für den Vornamen & den Nachnamen sowie eine Schaltfläche mit der Bezeichnung „say Hello“.

Dieses ist wirklich minimalistisch gehalten soll aber für dieses Tutorial ausreichen.

package de.swingtest;

import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.io.Serializable;

import javax.swing.*;

/**
 * Einfache Klasse zum erzeugen eines {@link JFrame} zum eingeben des Vornamens
 * sowie des Nachnamens. Beim klicken auf den {@link JButton} wird ein
 * {@link JOptionPane} Dialog angezeigt.
 * 
 * @author Stefan Draeger
 *
 */
public class SimpleSwingGui extends JFrame {

	/**
	 * Da die Klasse JFrame das Interface {@link HasGetTransferHandler}
	 * implementiert und dieses Interface wiederum das Interface
	 * {@link Serializable} implementiert sollte man eine eindeutige ID für den
	 * Prozess der Serialisierung hinzufügen.
	 */
	private static final long serialVersionUID = 3153235363948579984L;

	/** {@link JTextField} für die Eingabe des Vornamens **/
	private JTextField vornameTextField;

	/** {@link JTextField} für die Eingabe des Nachnamens **/
	private JTextField nameTextField;

	/**
	 * Konstruktor
	 */
	public SimpleSwingGui() {
		init();
	}

	/**
	 * Das {@link JFrame} einrichten.
	 */
	private void init() {
		this.setTitle("SimpleSwingGui");
		this.setBounds(0, 0, 350, 100);
		this.setLayout(new GridLayout(0, 2));
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		initComponents();
		this.setVisible(true);
	}

	/**
	 * Erzeugen der Eingabefelder und des Buttons sowie der {@link ActionListener}
	 */
	private void initComponents() {
		this.add(new JLabel(" Vorname"));
		vornameTextField = new JTextField();
		vornameTextField.setName("vornameTextField");
		this.add(vornameTextField);

		this.add(new JLabel(" Name"));
		nameTextField = new JTextField();
		nameTextField.setName("nameTextField");
		this.add(nameTextField);

		JButton sayHelloBtn = new JButton("say Hello");
		sayHelloBtn.setName("sayHelloBtn");
		sayHelloBtn.addActionListener(event -> {
			String message = String.format("Hallo %s %s", vornameTextField.getText(), nameTextField.getText());
			int result = JOptionPane.showConfirmDialog(SimpleSwingGui.this, message, "Hinweis...",
					JOptionPane.OK_CANCEL_OPTION);
			String resultMsg = String.format("Button %s wurde geklickt", result == JOptionPane.OK_OPTION ? "OK"
					: result == JOptionPane.CANCEL_OPTION ? "ABBRECHEN" : "-undefined-");
			System.out.println(resultMsg);
		});
		this.add(sayHelloBtn);
	}
}

AssertJ im Projekt einrichten

Da ich das Projekt mit Apache Maven erzeugt habe kann man hier bequem die Abhängigkeiten (Dependencies) des Projektes in der pom.xml hinzufügen.

<dependency>
   <groupId>org.assertj</groupId>
   <artifactId>assertj-swing-jide</artifactId>
   <version>3.8.0</version>
   <scope>test</scope>
</dependency>

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

Es werden zusätzlich die Abhängigkeiten für JUnit & Hamcrest benötigt.

<dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.12</version>
   <scope>test</scope>
</dependency>

<dependency>
   <groupId>org.hamcrest</groupId>
   <artifactId>hamcrest-core</artifactId>
   <version>1.3</version>
   <scope>test</scope>
</dependency>

Nachdem die Datei pom.xml um die oben genannten Abhängigkeiten erweitert wurde muss das Projekt neu gebaut werden, dazu wird der Befehl

mvn install

auf dem Projekt ausgeführt.

Ersten JUnit Test anlegen

Wollen wir also als Erstes die GUI mit den folgenden Funktionen testen:

  • Eingabe von Vorname & Nachname,
  • Betätigen der Schaltfläche,
  • Öffnen des Hinweisdialoges,
    • Betätigen der Schaltflächen „OK“ & „Abbrechen“
package de.swingtest;

import static org.junit.Assert.assertNotNull;

import java.awt.event.KeyEvent;

import org.assertj.swing.edt.GuiActionRunner;
import org.assertj.swing.finder.JOptionPaneFinder;
import org.assertj.swing.fixture.FrameFixture;
import org.assertj.swing.fixture.JButtonFixture;
import org.assertj.swing.fixture.JOptionPaneFixture;
import org.assertj.swing.fixture.JTextComponentFixture;
import org.assertj.swing.junit.testcase.AssertJSwingJUnitTestCase;
import org.junit.Test;

/**
 * Eine einfache Klasse zum Testen der SwingAnwendung {@link SimpleSwingGui}.
 * Der Test erweitert die Abstrakte Klasse
 * {@link org.assertj.swing.junit.testcase.AssertJSwingJUnitTestCase}. Mit dem
 * erweitern der Abstrakten Klasse müssen wir die Methode
 * {@link org.assertj.swing.junit.testcase.AssertJSwingJUnitTestCase#onSetUp}
 * implementieren.
 * 
 * Zusätzlich habe ich noch die Methode
 * {@link org.assertj.swing.junit.testcase.AssertJSwingJUnitTestCase#onTearDown}
 * implementiert damit ich in dieser dann den JUnit Test aufräumen kann.
 * 
 * 
 * @author Stefan Draeger
 * 
 */
public class TestSimpleSwingGui extends AssertJSwingJUnitTestCase {

	// Eine "Referenz" auf die SwingAnwendung.
	private FrameFixture window;

	@Override
	protected void onSetUp() {
		// Starten der SwingAnwendung
		SimpleSwingGui frame = GuiActionRunner.execute(() -> new SimpleSwingGui());
		window = new FrameFixture(robot(), frame);
		window.show();
	}

	@Override
	protected void onTearDown() {
		// Nach dem Beenden des Testfalls, alles Aufräumen.
		// Das heißt, es werden alle Fenster geschlossen,
		// es werden die Maustasten freigegeben,
		// es wird der Bildschirm freigegeben.
		window.cleanUp();
	}

	@Test
	public void shouldBeShowInfodialog() {
		// Das JTextField mit dem Namen "vornameTextField" suchen und merken.
		JTextComponentFixture vornameTextField = window.textBox("vornameTextField");
		// Wenn das Objekt nicht NULL ist dann gehts weiter...
		assertNotNull(vornameTextField);
		//In das JTextField die Buchstaben m,a,x eingeben.
		vornameTextField.pressAndReleaseKeys(KeyEvent.VK_M, KeyEvent.VK_A, KeyEvent.VK_X);

		// Das JTextField mit dem Namen "nameTextField" suchen und merken.
		JTextComponentFixture nachnameTextField = window.textBox("nameTextField");
		// Wenn das Objekt nicht NULL ist dann gehts weiter...
		assertNotNull(nachnameTextField);
		//In das JTextField die Buchstaben m,u,s,t,e,r,m,a,n,n eingeben.
		nachnameTextField.pressAndReleaseKeys(KeyEvent.VK_M, KeyEvent.VK_U, KeyEvent.VK_S, KeyEvent.VK_T, KeyEvent.VK_E,
				KeyEvent.VK_R, KeyEvent.VK_M, KeyEvent.VK_A, KeyEvent.VK_N, KeyEvent.VK_N);

		// Die Schaltfläche mit dem Namen "sayHelloBtn" suchen und merken.
		JButtonFixture sayHelloBtn = window.button("sayHelloBtn");
		// Wenn das Objekt nicht NULL ist dann gehts weiter...
		assertNotNull(sayHelloBtn);
		//Eine Klickaktion auf die Schaltfläche ausführen.
		sayHelloBtn.click();

		JOptionPaneFixture optionPane = JOptionPaneFinder.findOptionPane().withTimeout(10).using(robot());
		// Wenn das Objekt nicht NULL ist dann gehts weiter...
		assertNotNull(optionPane);
		// Wenn das Objekt "optionPane" nicht sichtbar ist dann wird eine Fehlermeldung "geworfen".
		optionPane.requireVisible();
		
		// Die Schaltfläche mit dem Text "OK" suchen und merken.
		JButtonFixture okBtn = optionPane.buttonWithText("OK");
		// Wenn das Objekt nicht NULL ist dann gehts weiter...
		assertNotNull(okBtn);
		//Eine Klickaktion auf die Schaltfläche ausführen.
		okBtn.click();

		//Eine Klickaktion auf die Schaltfläche ausführen.
		sayHelloBtn.click();

		optionPane = JOptionPaneFinder.findOptionPane().withTimeout(10).using(robot());
		// Wenn das Objekt nicht NULL ist dann gehts weiter...
		assertNotNull(optionPane);
		optionPane.requireVisible();
		
		// Die Schaltfläche mit dem Text "Abbrechen" suchen und merken.
		JButtonFixture abbrechenBtn = optionPane.buttonWithText("Abbrechen");
		// Wenn das Objekt nicht NULL ist dann gehts weiter...
		assertNotNull(abbrechenBtn);
		//Eine Klickaktion auf die Schaltfläche ausführen.
		abbrechenBtn.click();
	}

}

Video

Während der Ausführung des Testes sollte die Tastatur und die Maus nicht betätigt werden, denn damit würde man den Testfall zu stoppen bringen.

Ausführen eines einfachen AssertJ Testfalls aus Eclipse
Dieses Video auf YouTube ansehen.

Das Video habe ich in 1/4 der Geschwindigkeit abgespielt damit man etwas sieht. D.h. solltest du den Test nachbauen wirst du je nach Rechenleistung mehr oder weniger von der Ausführung sehen.

Ein komplexes Beispiel

Nachdem wir nun ein einfaches Beispiel erzeugt und erläutert haben wollen wir nun ein kleinen Businessprozess in einer Beispielanwendung „durchspielen“.

Die „Anwendung“ hat folgende Funktion:

  • aufnehmen einer Adresse,
  • aufnehmen eines Alters (nur ganze Zahlen zwischen 1 und 100),
  • Schaltfläche zum
    • übernehmen,
    • zurücksetzen der Daten
  • Tabelle welche die Werte
    • ID (Timestamp),
    • Adresse,
    • Alter,
    • Zeitstempel (Formatiert) anzeigt
  • Schaltflächen für
    • exportieren (CSV Format), oder
    • importieren (CSV Format) der Daten
    • Schließen der Anwendung
Eine etwas komplexere Swing Anwenundg zum Testen
Eine etwas komplexere Swing Anwenundg zum Testen

Quellcode

Die nachfolgende Klasse befindet sich im Projekt „SwingTest“ welches ich am Ende dieses Tutorials zum Download anbiete.

package de.swingtest.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;

import net.miginfocom.layout.AC;
import net.miginfocom.layout.LC;
import net.miginfocom.swing.MigLayout;

public class ComplexSwingGui extends JFrame {

	private static final long serialVersionUID = -2973208254912255252L;

	private JTextArea adresseTextArea;
	private JTextField alterTextField;
	private JLabel formatErrorLbl;
	private JButton addBtn;
	private JButton exportBtn;
	private DefaultTableModel model;

	public ComplexSwingGui() {
		init();
	}

	private void init() {
		this.setTitle("ComplexSwingGui");
		this.setBounds(0, 0, 500, 680);
		MigLayout layout = new MigLayout(new LC().wrapAfter(1), new AC().align("center"), new AC());
		this.setLayout(layout);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		initComponents();
		this.setVisible(true);
	}

	private void initComponents() {
		adresseTextArea = new JTextArea();
		adresseTextArea.setName("adresseTextArea");
		adresseTextArea.setPreferredSize(new Dimension(250, 50));
		this.add(getLblComponent("Adresse:", "adresseLbl", new JScrollPane(adresseTextArea)));

		alterTextField = new JTextField();
		alterTextField.setName("alterTextField");
		alterTextField.setPreferredSize(new Dimension(250, 25));
		alterTextField.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				char c = e.getKeyChar();
				boolean isCharAllowed = ((c >= '0') && (c <= '9') || (c == KeyEvent.VK_BACK_SPACE)
						|| (c == KeyEvent.VK_DELETE));
				if (!isCharAllowed) {
					getToolkit().beep();
					e.consume();
				}
			};

			@Override
			public void keyReleased(KeyEvent e) {
				boolean alterIsCorrect = false;
				try {
					String text = alterTextField.getText();
					if (!text.isEmpty()) {
						Integer.parseInt(text);
					}
					alterIsCorrect = true;
				} catch (Exception ex) {
					// ex.printStackTrace();
				} finally {
					addBtn.setEnabled(alterIsCorrect);
					formatErrorLbl.setVisible(!alterIsCorrect);
				}
			}

		});
		this.add(getLblComponent("Alter:", "alterLbl", alterTextField));

		formatErrorLbl = new JLabel("Es sind nur numerische Werte (0..9) von 1 bis 100 erlaubt.");
		formatErrorLbl.setName("formatErrorLbl");
		formatErrorLbl.setForeground(Color.RED);
		formatErrorLbl.setVisible(false);
		this.add(formatErrorLbl);

		JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
		btnPanel.setPreferredSize(new Dimension(450, 45));
		addBtn = new JButton("Hinzufügen");
		addBtn.setName("addBtn");
		addBtn.addActionListener(event -> {
			long millis = System.currentTimeMillis();
			model.addRow(new Object[] { String.valueOf(millis), adresseTextArea.getText(), alterTextField.getText(),
					getFormatedTimestamp(millis) });
		});

		btnPanel.add(addBtn);
		JButton resetBtn = new JButton("Zurücksetzen");
		resetBtn.setName("resetBtn");
		resetBtn.addActionListener(event -> {
			adresseTextArea.setText("");
			alterTextField.setText("");
		});
		btnPanel.add(resetBtn);
		this.add(btnPanel);

		model = new DefaultTableModel(new Object[] { "ID", "Adresse", "Alter", "Zeitstempel" }, 0);
		JTable table = new JTable(model);
		table.setName("dataTable");
		this.add(new JScrollPane(table));

		JPanel btnPanel2 = new JPanel(new FlowLayout(FlowLayout.RIGHT));
		btnPanel2.setPreferredSize(new Dimension(450, 45));

		exportBtn = new JButton("Exportieren");
		exportBtn.setName("exportBtn");
		exportBtn.addActionListener(event -> {
			JFileChooser fileChooser = new JFileChooser();
			int result = fileChooser.showSaveDialog(ComplexSwingGui.this);
			if (result == JFileChooser.APPROVE_OPTION) {
				File selectedFile = fileChooser.getSelectedFile();
				try (BufferedWriter bw = new BufferedWriter(new FileWriter(selectedFile))) {
					int rowCount = model.getRowCount();
					for (int i = 0; i < rowCount; i++) {
						long id = (long) Long.parseLong(model.getValueAt(i, 0).toString());
						String adresse = (String) model.getValueAt(i, 1);
						adresse = adresse.replaceAll("\n", " ");
						int alter = Integer.parseInt(model.getValueAt(i, 2).toString());
						String zeitstempel = (String) model.getValueAt(i, 3);
						String row = String.format("%d;%s;%d;%s\r\n", id, adresse, alter, zeitstempel);
						bw.write(row);
						bw.flush();
					}
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		});
		btnPanel2.add(exportBtn);

		JButton importBtn = new JButton("Importieren");
		importBtn.setName("importBtn");
		importBtn.addActionListener(event -> {
			JFileChooser fileChooser = new JFileChooser();
			int result = fileChooser.showOpenDialog(ComplexSwingGui.this);
			if (result == JFileChooser.APPROVE_OPTION) {
				File selectedFile = fileChooser.getSelectedFile();
				try (BufferedReader br = new BufferedReader(new FileReader(selectedFile))) {
					String line = "";
					while ((line = br.readLine()) != null) {
						System.out.println(line);
						String[] values = line.split(";");
						model.addRow(new Object[] { values[0], values[1], values[2], values[3] });
					}
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		});
		btnPanel2.add(importBtn);

		JButton clearBtn = new JButton("Tabelle leeren");
		clearBtn.setName("clearBtn");
		clearBtn.addActionListener(event -> {
			while (model.getRowCount() > 0) {
				model.removeRow(0);
			}
		});
		btnPanel2.add(clearBtn);

		JButton closeBtn = new JButton("Schließen");
		closeBtn.setName("closeBtn");
		closeBtn.addActionListener(event -> {
			System.exit(0);
		});
		btnPanel2.add(closeBtn);
		this.add(btnPanel2);
	}

	private JPanel getLblComponent(String lblBez, String lblName, JComponent comp) {
		JPanel pnl = new JPanel(new FlowLayout());
		JLabel lbl = new JLabel(lblBez);
		lbl.setName(lblName);
		lbl.setPreferredSize(new Dimension(150, 25));
		pnl.add(lbl);
		pnl.add(comp);
		return pnl;
	}

	private String getFormatedTimestamp(long millis) {
		DateFormat dateformat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
		return dateformat.format(new Date(millis));
	}
}

Erstellen der Testfälle

Es wird nun die Swing-Anwendung getestet. Dazu erzeuge ich ein neues Maven Projekt in Eclipse und werde dort die oben genannte Klasse hineinkopieren und verwenden.

Video

Im nachfolgenden YouTube Video möchte nun ausführlich erläutern wie man das Maven Projekt mit AssertJ, JUnit einrichtet.

AssertJ - Tutorial
Dieses Video auf YouTube ansehen.

Sollte eine Frage offen bleiben dann kannst du mir diese gerne als Kommentar oder per E-Mail hinterlassen.

package de.swingtest;

import java.awt.event.KeyEvent;
import java.io.File;

import org.assertj.swing.core.matcher.JLabelMatcher;
import org.assertj.swing.edt.GuiActionRunner;
import org.assertj.swing.fixture.FrameFixture;
import org.assertj.swing.fixture.JButtonFixture;
import org.assertj.swing.fixture.JFileChooserFixture;
import org.assertj.swing.fixture.JLabelFixture;
import org.assertj.swing.fixture.JTableFixture;
import org.assertj.swing.fixture.JTextComponentFixture;
import org.assertj.swing.junit.testcase.AssertJSwingJUnitTestCase;
import org.junit.Test;

import de.swingtest.gui.ComplexSwingGui;

public class TestComplexSwingGui extends AssertJSwingJUnitTestCase {

	private static final String EXPORT_DATEI = "diesIstEinDateiname.csv";

	private FrameFixture window;

	int[] vorname = { KeyEvent.VK_A, KeyEvent.VK_X, KeyEvent.VK_SPACE };
	int[] nachname = { KeyEvent.VK_U, KeyEvent.VK_S, KeyEvent.VK_T, KeyEvent.VK_E, KeyEvent.VK_R, KeyEvent.VK_M,
			KeyEvent.VK_A, KeyEvent.VK_N, KeyEvent.VK_N, KeyEvent.VK_ENTER };
	int[] strasse = { KeyEvent.VK_U, KeyEvent.VK_S, KeyEvent.VK_T, KeyEvent.VK_E, KeyEvent.VK_R, KeyEvent.VK_S,
			KeyEvent.VK_T, KeyEvent.VK_R, KeyEvent.VK_A, KeyEvent.VK_S, KeyEvent.VK_S, KeyEvent.VK_E, KeyEvent.VK_SPACE,
			KeyEvent.VK_1, KeyEvent.VK_ENTER };
	int[] ortTeil1 = { KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_3, KeyEvent.VK_4, KeyEvent.VK_5, KeyEvent.VK_SPACE };
	int[] ortTeil2 = { KeyEvent.VK_U, KeyEvent.VK_S, KeyEvent.VK_T, KeyEvent.VK_E, KeyEvent.VK_R, KeyEvent.VK_S,
			KeyEvent.VK_T, KeyEvent.VK_A, KeyEvent.VK_D, KeyEvent.VK_T };

	boolean firstRun = true;
	long timestamp = 0L;

	@Override
	protected void onSetUp() {
		ComplexSwingGui frame = GuiActionRunner.execute(() -> new ComplexSwingGui());
		window = new FrameFixture(robot(), frame);
		window.show();
		if (firstRun) {
			timestamp = System.currentTimeMillis();
			firstRun = false;
		}

	}

	@Override
	protected void onTearDown() {
		window.cleanUp();
		// Export/Impot Datei löschen
		File fileToDetele = new File(EXPORT_DATEI);
		fileToDetele.delete();
	}

	// @Test
	public void componentsShouldBeVisible() {
		JLabelFixture adresseLbl = window.label("adresseLbl");
		adresseLbl.requireVisible();
		adresseLbl.requireText("Adresse:");

		JTextComponentFixture adresseTextBox = window.textBox("adresseTextArea");
		adresseTextBox.requireVisible();

		JLabelFixture alterLbl = window.label("alterLbl");
		alterLbl.requireVisible();
		alterLbl.requireText("Alter:");

		JTextComponentFixture alterTextBox = window.textBox("alterTextField");
		alterTextBox.requireVisible();

		JLabelFixture formatErrorLbl = window.label(JLabelMatcher.withName("formatErrorLbl"));
		formatErrorLbl.requireNotVisible();
		formatErrorLbl.requireText("Es sind nur numerische Werte (0..9) von 1 bis 100 erlaubt.");

		JButtonFixture addBtn = window.button("addBtn");
		addBtn.requireVisible();
		addBtn.requireEnabled();
		addBtn.requireText("Hinzufügen");

		JButtonFixture resetBtn = window.button("resetBtn");
		resetBtn.requireVisible();
		resetBtn.requireEnabled();
		resetBtn.requireText("Zurücksetzen");

		JTableFixture table = window.table("dataTable");
		table.requireVisible();

		JButtonFixture exportBtn = window.button("exportBtn");
		exportBtn.requireVisible();
		exportBtn.requireEnabled();
		exportBtn.requireText("Exportieren");

		JButtonFixture importBtn = window.button("importBtn");
		importBtn.requireVisible();
		importBtn.requireEnabled();
		importBtn.requireText("Importieren");

		JButtonFixture clearBtn = window.button("clearBtn");
		clearBtn.requireVisible();
		clearBtn.requireEnabled();
		clearBtn.requireText("Tabelle leeren");

		JButtonFixture closeBtn = window.button("closeBtn");
		closeBtn.requireVisible();
		closeBtn.requireEnabled();
		closeBtn.requireText("Schließen");
	}

	@Test
	public void tableHasCorrectColumns() {
		JTableFixture table = window.table("dataTable");
		table.requireVisible();
		table.requireColumnCount(4);

		JTableFixture columnID = table.requireColumnNamed("ID");
		columnID.requireVisible();
		JTableFixture columnAdresse = table.requireColumnNamed("Adresse");
		columnAdresse.requireVisible();
		JTableFixture columnAlter = table.requireColumnNamed("Alter");
		columnAlter.requireVisible();
		JTableFixture columnZeitstempel = table.requireColumnNamed("Zeitstempel");
		columnZeitstempel.requireVisible();
	}

	@Test
	public void shouldBeAddData() {
		addData();
	}

	@Test
	public void shouldBeExportData() {
		addData();
		exportData();
	}

	@Test
	public void shouldBeRemoveData() {
		addData();
		removeData();
	}

	@Test
	public void shouldBeSImportData() {
		addData();
		exportData();
		removeData();
		importData();
	}

	@Test
	public void shouldBeSImportDataClearForm() {
		removeData();
		importData();
	}

	private void addData() {
		JTextComponentFixture adresseTextBox = window.textBox("adresseTextArea");
		typeUpperCaseLetter(KeyEvent.VK_M, adresseTextBox);
		adresseTextBox.pressAndReleaseKeys(vorname);
		typeUpperCaseLetter(KeyEvent.VK_M, adresseTextBox);
		adresseTextBox.pressAndReleaseKeys(nachname);
		typeUpperCaseLetter(KeyEvent.VK_M, adresseTextBox);
		adresseTextBox.pressAndReleaseKeys(strasse);
		adresseTextBox.pressAndReleaseKeys(ortTeil1);
		typeUpperCaseLetter(KeyEvent.VK_M, adresseTextBox);
		adresseTextBox.pressAndReleaseKeys(ortTeil2);

		adresseTextBox.requireText("Max Mustermann\nMusterstrasse 1\n12345 Musterstadt");

		JTextComponentFixture alterTextBox = window.textBox("alterTextField");
		alterTextBox.pressAndReleaseKeys(KeyEvent.VK_3, KeyEvent.VK_1);

		alterTextBox.requireText("31");
		JLabelFixture label = window.label(JLabelMatcher.withName("formatErrorLbl"));
		label.requireNotVisible();

		JButtonFixture addBtn = window.button("addBtn");
		addBtn.requireEnabled();
		addBtn.click();

		JTableFixture table = window.table("dataTable");
		table.requireRowCount(1);
	}

	private void exportData() {
		JButtonFixture exportBtn = window.button("exportBtn");
		exportBtn.requireEnabled();
		exportBtn.click();

		JFileChooserFixture fileChooserFixture = window.fileChooser();
		fileChooserFixture.requireVisible();

		fileChooserFixture.fileNameTextBox().setText(EXPORT_DATEI);
		fileChooserFixture.approveButton().click();
	}

	private void removeData() {
		JButtonFixture clearBtn = window.button("clearBtn");
		clearBtn.click();
		JTableFixture table = window.table("dataTable");
		table.requireRowCount(0);
	}

	private void importData() {
		JButtonFixture importBtn = window.button("importBtn");
		importBtn.requireEnabled();
		importBtn.click();

		JFileChooserFixture fileChooserFixture = window.fileChooser();
		fileChooserFixture.requireVisible();

		fileChooserFixture.fileNameTextBox().setText(EXPORT_DATEI);
		fileChooserFixture.approveButton().click();

		JTableFixture table = window.table("dataTable");
		table.requireRowCount(1);
	}

	private void typeUpperCaseLetter(int letter, JTextComponentFixture textBox) {
		textBox.pressKey(KeyEvent.VK_SHIFT);
		textBox.pressAndReleaseKeys(letter);
		textBox.releaseKey(KeyEvent.VK_SHIFT);
	}

}

Troubleshooting

Fehlerhafte Darstellung des Fensters

Beim Erzeugen der TestCases ist mir zuerst aufgefallen, dass das Fenster nicht korrekt dargestellt wurde. Ich habe zuerst das java.awt.FlowLayout verwendet, wenn die Anwendung „normal“ gestartet wurde sah alles gut aus (siehe Bild oben) jedoch beim  Ausführen des Testfalls wurde das komplette Layout verändert.

Verändertes Layout während der Ausführung von AssertJ Testfall.
Verändertes Layout während der Ausführung von AssertJ Testfall.

Nach einer kurzes suche, habe ich zu diesem Fehler nichts gefunden aber viele Beispiele zu diesem Testframework und dort habe ich gesehen das nirgends das FlowLayout verwendet wird. Daher gehe ich mal davon aus das der Fehler bekannt ist.

Ein Wechsel vom FlowLayout zum MigLayout brachte den erhofften Erfolg.

Diese Lösung ist für das erzeugen von Testfällen in bereits bestehende Projekte eigentlich ein K.O. Kriterium.

ComponentLookupException: Unable to find component using matcher

Wenn man einen Test auf einer java.awt.Component ausführen möchte, so „holt“ man sich das Objekt vom aktuellen Fenster mit:

JLabelFixture label = window.label("testLbl");
label.requireNotVisible();

Wenn dieses Objekt aber nicht sichtbar ist dann erhält man folgende Exception

org.assertj.swing.exception.ComponentLookupException: Unable to find component using matcher org.assertj.swing.core.NameMatcher[name='testLbl', type=javax.swing.JLabel, requireShowing=true].

Component hierarchy:
de.swingtest.gui.ComplexSwingGui[name='frame0', title='ComplexSwingGui', enabled=true, visible=true, showing=true]
  javax.swing.JRootPane[]

Der wichtige Teil dieser Exception ist dass, das Framework erwartet das die Komponente sichtbar ist („requireShowing=true“).

Wie kann man also eine Komponente suchen (und im Idealfall finden) welche nicht sichtbar ist?

Dafür gibt es die Matcher für so ziemlich jede JComponent gibt es einen entsprechenden Matcher, diese beginnen mit <JComponentName>Matcher.
In diesem Fall musste ich den JLabelMatcher verwenden.

JLabelFixture label = window.label(JLabelMatcher.withName("testLbl"));
label.requireNotVisible();

Nun wurde die Komponente gefunden under Test war „grün“.

Download

Damit man bequem das gesamte Tutorial durchspielen kann, möchte ich hier nun das Eclipse Projekt zum Download anbieten.

Java Swing GUI Test mit AssertJ – Eclipse ProjektHerunterladen

Quellen und Referenzen

Auf der Seite der Entwickler findet man einen Link zu den Java Doc’s  sowie zu den Seiten wie man das Testframework einrichtet.

Fazit

Mit dem Testframework AssertJ und JUnit kann man relativ einfach und vor allem schnell einen Testfall erzeugen. Da ich in meinen Projekten immer (bzw. wenn möglich) Apache Maven verwenden kann ich diese Testfälle einfach beim Build automatisch starten.

Schreibe einen Kommentar Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Fragen oder Feedback?

Du hast eine Idee, brauchst Hilfe oder möchtest Feedback loswerden?
Support-Ticket erstellen

Newsletter abonnieren

Bleib auf dem Laufenden: Erhalte regelmäßig Updates zu neuen Projekten, Tutorials und Tipps rund um Arduino, ESP32 und mehr – direkt in dein Postfach.

Jetzt Newsletter abonnieren

Unterstütze meinen Blog

Wenn dir meine Inhalte gefallen, freue ich mich über deine Unterstützung auf Tipeee.
So hilfst du mit, den Blog am Leben zu halten und neue Beiträge zu ermöglichen.

draeger-it.blog auf Tipeee unterstützen

Vielen Dank für deinen Support!
– Stefan Draeger

Kategorien

Tools

  • Unix-Zeitstempel-Rechner
  • ASCII Tabelle
  • Spannung, Strom, Widerstand und Leistung berechnen
  • Widerstandsrechner
  • 8×8 LED Matrix Tool
  • 8×16 LED Matrix Modul von Keyestudio
  • 16×16 LED Matrix – Generator

Links

Blogverzeichnis Bloggerei.de TopBlogs.de das Original - Blogverzeichnis | Blog Top Liste Blogverzeichnis trusted-blogs.com

Stefan Draeger
Königsberger Str. 13
38364 Schöningen

Tel.: 01778501273
E-Mail: info@draeger-it.blog

Folge mir auf

  • Impressum
  • Datenschutzerklärung
  • Disclaimer
  • Cookie-Richtlinie (EU)
©2025 Technik Blog | Built using WordPress and Responsive Blogily theme by Superb
Cookie-Zustimmung verwalten
Wir verwenden Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wir tun dies, um das Surferlebnis zu verbessern und um personalisierte Werbung anzuzeigen. Wenn Sie diesen Technologien zustimmen, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn Sie Ihre Zustimmung nicht erteilen oder zurückziehen, können bestimmte Funktionen beeinträchtigt werden.
Funktional Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt. Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.
Optionen verwalten Dienste verwalten Verwalten von {vendor_count}-Lieferanten Lese mehr über diese Zwecke
Einstellungen anzeigen
{title} {title} {title}