SWTBot is a tool for automating UI tests of Eclipse plug-ins. The document covers setting up SWTBot, challenges of UI testing, exercises in using the SWTBot API to find widgets and perform actions, best practices like using abstractions and page objects, and tips for logging and timeouts. Key points include using matchers to find widgets robustly, ensuring thread safety, and modeling capabilities rather than UI elements.
16. All exercises focus around...
creating a java project “MyFirstProject”
create a java class
type in a program that prints “Hello, World”
execute the program
verify that the program printed “Hello, World”
24. Create a simple Test
@RunWith(SWTBotJunit4ClassRunner.class)
public class Test01 {
@Test
public void thisOneFails() throws Exception {
fail("this test fails");
}
@Test
public void thisOnePasses() throws Exception {
pass();
}
}
27. Finding widgets
SWTWorkbenchBot bot = new SWTWorkbenchBot();
SWTBot[Widget] widget = bot.[widget][With][Matcher(s)]();
SWTBotText text = bot.textWithLabel("Username:");
Think of the bot as something that can
do tasks for you (e.g., find widgets, click
on them, etc.)
28. Finding obscure widgets
Use matchers and WidgetMatcherFactory!
withLabel("Username:") Matcher pushButtonToProceedToNextStep = allOf(
withMnemonic("Finish") widgetOfType(Button.class),
withText("&Finish") withStyle(SWT.PUSH),
inGroup("Billing Address") withRegex("Proceed to step (.*) >")
widgetOfType(Button.class) );
withStyle(SWT.RADIO)
withTooltip("Save All") SWTBotButton button = new SWTBotButton(
withRegex("Welcome, (.*)") (Button)
bot.widget(pushButtonToProceedToNextStep)
);
29. Performing actions
button.click() // click on a button
// chain methods to make them concise
bot.menu("File").menu("New").menu("Project...").click();
tree.expand("Project").expand("src").expand("Foo.java").contextMenu("Delete").clic
k(); // delete Foo.java
list.select("Orange", "Banana", "Mango"); // make a selection in a list
list.select(2, 5, 6); // make a selection using indexes
31. Objectives
Get introduced to the SWTBot API
SWTWorkbenchBot is your friend
Tasks
beforeClass() - close the welcome view
canCreateANewProject() - create a java project
Run the test!
Ensure SWTBot logging works
32. beforeClass()
@BeforeClass
public static void beforeClass() throws Exception {
bot = new SWTWorkbenchBot();
bot.viewByTitle("Welcome").close();
}
39. Find all widgets
Depth first traversal of UI elements
1.Find top level widgets
1.Find children of each widget
2.For each child do (1) and (2)
40. Finding widgets
public SWTBotTree treeWithLabelInGroup(String l, String g, int i) {
// create the matcher
Matcher matcher =
allOf(
widgetOfType(Tree.class), withLabel(l), inGroup(g)
);
// find the widget, with redundancy built in
Tree tree = (Tree) widget(matcher, index);
// create a wrapper for thread safety
// and convinience APIs
return new SWTBotTree(tree, matcher);
}
42. Thread Safety (Query state)
public class SWTBotCheckBox {
public boolean isChecked() {
// evaluate a result on the UI thread
return syncExec(new BoolResult() {
public Boolean run() {
return widget.getSelection();
}
});
}
}
43. Thread Safety(change state)
public class SWTBotCheckBox {
public void select() {
asyncExec(new VoidResult() {
public void run() {
widget.setSelection(true);
}
});
notifyListeners();
}
protected void notifyListeners() {
notify(SWT.MouseDown);
notify(SWT.MouseUp);
notify(SWT.Selection);
}
}
45. Objectives
Tasks
canCreateANewJavaClass()
create a new java class: HelloWorld
Source folder: MyFirstProject/src
Package: org.eclipsecon.project
Click Finish!
Run the test!
50. canExecuteJavaApplication()
@Test
public void canExecuteJavaApplication() throws Exception {
bot.menu("Run").menu("Run").click();
// FIXME: verify that the program printed 'Hello World'
}
53. Objectives
Learn about SWTBot and the Matcher API
Tasks
canExecuteJavaApplication()
Run the Java application
Grab the Console view
Verify that “Hello World” is printed
Run the tests!
55. Matchers
Under the covers, SWTBot uses Hamcrest
Hamcrest is a framework for writing ‘match’ rules
SWTBot is essentially all about match rules
bot.widget(WidgetMatcherFactory.widgetO
fType(StyledText.class),
consoleViewComposite);
60. Objectives
Learn about waiting and SWTBot Condition API
Tasks
afterClass()
Select the Package Explorer view
Select MyFirstProject
Right click and delete the project
Run the test!
61. afterClass()
@AfterClass
public static void afterClass() throws Exception {
SWTBotView packageExplorerView = bot.viewByTitle("Package Explorer");
packageExplorerView.show();
Composite packageExplorerComposite = (Composite) packageExplorerView.getWidget();
Tree swtTree =
(Tree) bot.widget(WidgetMatcherFactory.widgetOfType(Tree.class), packageExplorerComposite);
SWTBotTree tree = new SWTBotTree(swtTree);
tree.select("MyFirstProject");
bot.menu("Edit").menu("Delete").click();
// the project deletion confirmation dialog
SWTBotShell shell = bot.shell("Delete Resources");
shell.activate();
bot.checkBox("Delete project contents on disk (cannot be undone)").select();
bot.button("OK").click();
bot.waitUntil(shellCloses(shell));
}
62. Handling long operations
describe a condition
poll for the condition at intervals
wait for it to evaluate to true or false
of course there’s a timeout
SWTBot has an ICondition
67. Abstractions are good!
They simplify writing tests for QA folks
Build page objects for common “services”
Project Explorer
The Editor
The Console View
The main menu bar, tool bar
71. Sample Domain Object
public class JavaProject {
public JavaProject create(String projectName){
// create a project and return it
}
public JavaProject delete(){
// delete the project and return it
}
public JavaClass createClass(String className){
// create a class and return it
}
}
73. Page Objects?
Represent the services offered by the page to the test
developer
Internally knows the details about how these services
are offered and the details of UI elements that offer
them
Return other page objects to model the user’s journey
through the application
Different results of the same operation modeled
74. Page Objects... should not
Expose details about user interface elements
Make assertions about the state of the UI
75. A Sample Page Object
public class LoginPage {
public HomePage loginAs(String user, String pass) {
// ... clever magic happens here
}
public LoginPage loginAsExpectingError(String user, String pass) {
// ... failed login here, maybe because one or both of
// username and password are wrong
}
public String getErrorMessage() {
// So we can verify that the correct error is shown
}
}
76. Using Page Objects
// the bad test
public void testMessagesAreReadOrUnread() {
Inbox inbox = new Inbox(driver);
inbox.assertMessageWithSubjectIsUnread("I like cheese");
inbox.assertMessageWithSubjectIsNotUndread("I'm not fond of tofu");
}
// the good test
public void testMessagesAreReadOrUnread() {
Inbox inbox = new Inbox(driver);
assertTrue(inbox.isMessageWithSubjectIsUnread("I like cheese"));
assertFalse(inbox.isMessageWithSubjectIsUnread("I'm not fond of tofu"));
}
77. LoginPage login = new LoginPage();
HomePage home = login.loginAs("username", "secret");
SearchPage search = home.searchFor("swtbot");
assertTrue(search.containsResult("http://eclipse.org/swtbot"));
79. Objectives
Learn about page and domain objects
Tasks
Create a NewProjectBot
Opens the new project wizard
Allows you to set the project name and click finish
Modify canCreateANewJavaProject()
Run the test!
80. NewProjectBot
public class NewProjectBot {
private static SWTWorkbenchBot bot;
public NewProjectBot() {
bot.menu("File").menu("New").menu("Project...").click();
bot.shell("New Project").activate();
bot.tree().select("Java Project");
bot.button("Next >").click();
}
public void setProjectName(String projectName) {
bot.textWithLabel("Project name:").setText(projectName);
}
public void finish() {
bot.button("Finish").click();
}
}
81. Modify Project Wizard Code
@Test
public void canCreateANewJavaProject() throws Exception {
// use the NewProjectBot abstraction
NewProjectBot newProjectBot = new NewProjectBot();
newProjectBot.setProjectName("MyFirstProject");
newProjectBot.finish();
assertProjectCreated();
}
83. Logging
Turn logging on always, it helps :)
http://wiki.eclipse.org/SWTBot/
FAQ#How_do_I_configure_log4j_to_turn_on
_logging_for_SWTBot.3F
84. Slow down executions
Useful for debugging at times...
SWTBotPreferences.PLAYBACK_DELAY
-Dorg.eclipse.swtbot.playback.delay=20
or via code...
long oldDelay = SWTBotPreferences.PLAYBACK_DELAY;
// increase the delay
SWTBotPreferences.PLAYBACK_DELAY = 10;
// do whatever
// set to the original timeout of 5 seconds
SWTBotPreferences.PLAYBACK_DELAY = oldDelay;
85. Changing Timeout
SWTBotPreferences.TIMEOUT
-Dorg.eclipse.swtbot.search.timeout=10000
or via code...
long oldTimeout = SWTBotPreferences.TIMEOUT;
// increase the timeout
SWTBotPreferences.TIMEOUT = 10000;
// do whatever
// set to the original timeout of 5 seconds
SWTBotPreferences.TIMEOUT = oldTimeout;
86. Identifiers
In our examples, we used labels as matchers mostly
SWTBot allows you to use IDs as matchers
IDs are less brittle than labels!
text.setData(SWTBotPreferences.DEFAULT_
KEY,”id”)
bot.textWithId(“id”)
87. Page Objects
Use Page Objects to simplify writing tests
Allows more people than just normal devs to write tests
Should:
* Represent the services offered by the page to the test developer
* Internally knows the details about how these services are offered and the details of UI elements that offer them
* Return other page objects to model the user’s journey through the application
* Different results of the same operation modeled differently
Should not:
* Expose details about user interface elements
* Make assertions about the state of the UI