SWTBot Tutorial

14,208 views 89 slides Jun 25, 2010
Slide 1
Slide 1 of 89
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39
Slide 40
40
Slide 41
41
Slide 42
42
Slide 43
43
Slide 44
44
Slide 45
45
Slide 46
46
Slide 47
47
Slide 48
48
Slide 49
49
Slide 50
50
Slide 51
51
Slide 52
52
Slide 53
53
Slide 54
54
Slide 55
55
Slide 56
56
Slide 57
57
Slide 58
58
Slide 59
59
Slide 60
60
Slide 61
61
Slide 62
62
Slide 63
63
Slide 64
64
Slide 65
65
Slide 66
66
Slide 67
67
Slide 68
68
Slide 69
69
Slide 70
70
Slide 71
71
Slide 72
72
Slide 73
73
Slide 74
74
Slide 75
75
Slide 76
76
Slide 77
77
Slide 78
78
Slide 79
79
Slide 80
80
Slide 81
81
Slide 82
82
Slide 83
83
Slide 84
84
Slide 85
85
Slide 86
86
Slide 87
87
Slide 88
88
Slide 89
89

About This Presentation

A set of slides to teach people about SWTBot.


Slide Content

SWTBot Essentials
Chris Aniszczykhttp://aniszczyk.org
[email protected]

introduction
exercises
setting up the environment
SWTBot API
how does it work?
conclusion and Q&A
Agenda

UI Testing
It’s not easy, natural brittleness...
manual vs. automated testing
deciding what to test

UI Testing Challenges

Challenges
Identifying Controls

Challenges
Similar looking controls

Similar looking controls

Moving controls

Challenges
Sending “events” to controls

Challenges
Manage SWT Threading

Challenges
Tests to be non-blocking

Challenges
Run in a separate thread
Still manage synchronization between threads

Challenges
Internationalization (i18n) and Localization (L10n)

Challenges
Readability

Hands on exercises!
Learn by doing.

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”All exercises focus around...

Exercise 0: Setup
Test01.java

Setup SWTBot
Run a test with the SWTBot launch configuration
Objectives

Use Eclipse 3.6
Install SWTBot via update site http://www.eclipse.org/swtbot/downloads.php
Setting up the Environment

“org.eclipse.swtbot.examples”
Create a plugin project

org.eclipse.ui
org.eclipse.core.runtime
org.eclipse.swtbot.eclipse.finder
org.eclipse.swtbot.junit4_x
org.eclipse.swtbot.swt.finder
org.junit4
Setup Dependencies

Create a new plugin project

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();
}
}

Run the first test

SWTBot API Introduction

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.)

Finding obscure widgets
withLabel("Username:")
withMnemonic("Finish")
withText("&Finish")
inGroup("Billing Address")
widgetOfType(Button.class)
withStyle(SWT.RADIO)
withTooltip("Save All")
withRegex("Welcome, (.*)")
Use matchers and WidgetMatcherFactory !
Matcher pushButtonToProceedToNextStep = allOf(
widgetOfType(Button.class),
withStyle(SWT.PUSH),
withRegex("Proceed to step (.*) >")
);
SWTBotButton button = new SWTBotButton(
(Button)
bot.widget(pushButtonToProceedToNextStep)
);

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

Exercise 1: Hello SWTBot
SWTBotTest01.java

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
Objectives

beforeClass()
@BeforeClass
public static void beforeClass() throws Exception {
bot = new SWTWorkbenchBot();
bot.viewByTitle("Welcome").close();
}

canCreateANewProject()
@Test
public void canCreateANewJavaProject() throws Exception {
bot.menu("File").menu("New").menu("Project...").click();
SWTBotShell shell = bot.shell("New Project");
shell.activate();
bot.tree().select("Java Project");
bot.button("Next >").click();
bot.textWithLabel("Project name:").setText("MyFirstProject");
bot.button("Finish").click();
}

Seeing log messages?

How does it work? Magic?

!"#$%&'(
)"*#(+,,(
-"#$%&'(

Redundancy and Failure
Proofing

!"#$%&'(
)"*#(+,,(
-"#$%&'(

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)

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);
}

Thread Safety
Tests should run in non-ui thread
query state of a widget
change state of a widget

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();
}
});
}
}

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);
}
}

Exercise 2: More SWTBot
SWTBotTest02.java

Tasks
canCreateANewJavaClass()
create a new java class: HelloWorld
Source folder: MyFirstProject/src
Package: org.eclipsecon.project
Click Finish!
Run the test!
Objectives

canCreateANewJavaClass()
@Test
public void canCreateANewJavaClass() throws Exception {
bot.toolbarDropDownButtonWithTooltip("New Java Class").menuItem("Class").click();
bot.shell("New Java Class").activate();
bot.textWithLabel("Source folder:").setText("MyFirstProject/src");
bot.textWithLabel("Package:").setText("org.eclipsecon.project");
bot.textWithLabel("Name:").setText("HelloWorld");
bot.button("Finish").click();
}

Exercise 3: SWTBotEditor
SWTBotTest03.java

Tasks
canTypeInTextInAJavaClass()
open a file: HelloWorld.java
set some text in the Java file
canExecuteJavaApplication ()
Run the Java application
Run the test!
Objectives

canTypeInTextInAJavaClass()
@Test
public void canTypeInTextInAJavaClass() throws Exception {
Bundle bundle = Activator.getContext().getBundle();
String contents =
FileUtils.read(bundle.getEntry("test-files/HelloWorld.java"));
SWTBotEditor editor = bot.editorByTitle("HelloWorld.java");
SWTBotEclipseEditor e = editor.toTextEditor();
e.setText(contents);
editor.save();
}

canExecuteJavaApplication()
@Test
public void canExecuteJavaApplication() throws Exception {
bot.menu("Run").menu("Run").click();

// FIXME: verify that the program printed 'Hello World'
}

SWTBotEditor

Exercise 4: Matchers
SWTBotTest04.java

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!
Objectives

canExecuteJavaApplication()
@Test
public void canExecuteJavaApplication() throws Exception {
bot.menu("Run").menu("Run").click();
SWTBotView view = bot.viewByTitle("Console");
Widget consoleViewComposite = view.getWidget();
StyledText console =
bot.widget(WidgetMatcherFactory.widgetOfType(StyledText.class),
consoleViewComposite);
SWTBotStyledText styledText = new SWTBotStyledText(console);
assertTextContains("Hello World", styledText);
}

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);

WidgetMatcherFactory

Creating matchers
WidgetMatcherFactory is your friend!
withText("Finish")
withLabel("Username:")
withRegex("Proceed to step (.*)")
widgetOfType(Button.class)
withStyle(SWT.ARROW, "SWT.ARROW")

Combining matchers
allOf(matchers...)
anyOf(matchers...)
not(matcher)
allOf(anyOf(matchers...), matchers...)

Exercise 5: Waiting
SWTBotTest05.java

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!
Objectives

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));
}

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

ICondition

Handling Waits
private void waitUntil(ICondition condition, long timeout, long interval) {
long limit = System.currentTimeMillis() + timeout;
condition.init((SWTBot) this);
while (true) {
try {
if (condition.test())
return;
} catch (Throwable e) {
// do nothing
}
sleep(interval);
if (System.currentTimeMillis() > limit)
throw new TimeoutException("Timeout after: " + timeout);
}
}

Conditions is your friend

Building Abstractions

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

Model Capabilities
Create a project
Delete a project
Create a class
Execute a class
more...

Domain Objects

Domain Objects?
Represent the operations that can be performed on
concepts

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
}
}

Page Objectshttp://code.google.com/p/webdriver/wiki/PageObjects

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

Page Objects... should not
Expose details about user interface elements
Make assertions about the state of the UI

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
}
}

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"));
}

LoginPage login = new LoginPage();
HomePage home = login.loginAs("username", "secret");
SearchPage search = home.searchFor("swtbot");
assertTrue(search.containsResult(" http://eclipse.org/swtbot "));

Exercise 6: Abstractions
SWTBotTest06.java

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!
Objectives

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();
}
}

Modify Project Wizard Code
@Test
public void canCreateANewJavaProject() throws Exception {
// use the NewProjectBot abstraction
NewProjectBot newProjectBot = new NewProjectBot();
newProjectBot.setProjectName("MyFirstProject");
newProjectBot.finish();
assertProjectCreated();
}

Best Practices & Tips

Turn logging on always, it helps :)
http://wiki.eclipse.org/SWTBot/
FAQ#How_do_I_configure_log4j_to_turn_on
_logging_for_SWTBot.3F
Logging

Useful for debugging at times...
SWTBotPreferences.PLAYBACK_DELAY
-Dorg.eclipse.swtbot.playback.delay=20
or via code...
Slow down executions
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;

SWTBotPreferences.TIMEOUT
-Dorg.eclipse.swtbot.search.timeout=10000
or via code...
Changing Timeout
long oldTimeout = SWTBotPreferences.TIMEOUT;
// increase the timeout
SWTBotPreferences.TIMEOUT = 10000;
// do whatever
// set to the original timeout of 5 seconds
SWTBotPreferences.TIMEOUT = oldTimeout;

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”)
Identifiers

Use Page Objects to simplify writing tests
Allows more people than just normal devs to write tests
Page Objects

More SWTBot?
Eclipse Forms support in HEAD
GEF support is available for graphical editor testing

Questions ?
newsgroup: news://news.eclipse.org/eclipse.swtbot
web: eclipse.org/swtbot