Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

UI Testing Challenges

Slide 5

Slide 5 text

Challenges Identifying Controls

Slide 6

Slide 6 text

Challenges Similar looking controls

Slide 7

Slide 7 text

Similar looking controls

Slide 8

Slide 8 text

Moving controls

Slide 9

Slide 9 text

Challenges Sending “events” to controls

Slide 10

Slide 10 text

Challenges Manage SWT Threading

Slide 11

Slide 11 text

Challenges Tests to be non-blocking

Slide 12

Slide 12 text

Challenges Run in a separate thread Still manage synchronization between threads

Slide 13

Slide 13 text

Challenges Internationalization (i18n) and Localization (L10n)

Slide 14

Slide 14 text

Challenges Readability

Slide 15

Slide 15 text

Hands on exercises! Learn by doing.

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

Exercise 0: Setup Test01.java

Slide 18

Slide 18 text

Setup SWTBot Run a test with the SWTBot launch configuration Objectives

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Create a new plugin project

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Run the first test

Slide 26

Slide 26 text

SWTBot API Introduction

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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").click(); // delete Foo.java list.select("Orange", "Banana", "Mango"); // make a selection in a list list.select(2, 5, 6); // make a selection using indexes

Slide 30

Slide 30 text

Exercise 1: Hello SWTBot SWTBotTest01.java

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Seeing log messages?

Slide 35

Slide 35 text

How does it work? Magic?

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

Redundancy and Failure Proofing

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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)

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Exercise 2: More SWTBot SWTBotTest02.java

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

Exercise 3: SWTBotEditor SWTBotTest03.java

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

canExecuteJavaApplication() @Test public void canExecuteJavaApplication() throws Exception { bot.menu("Run").menu("Run").click(); // FIXME: verify that the program printed 'Hello World' }

Slide 51

Slide 51 text

SWTBotEditor

Slide 52

Slide 52 text

Exercise 4: Matchers SWTBotTest04.java

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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.widgetOfType (StyledText.class), consoleViewComposite);

Slide 56

Slide 56 text

WidgetMatcherFactory

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Exercise 5: Waiting SWTBotTest05.java

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

ICondition

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Conditions is your friend

Slide 66

Slide 66 text

Building Abstractions

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

Domain Objects

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

Page Objects http://code.google.com/p/webdriver/wiki/PageObjects

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

Exercise 6: Abstractions SWTBotTest06.java

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

Best Practices & Tips

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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;

Slide 85

Slide 85 text

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;

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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