Skip to content

Instantly share code, notes, and snippets.

@joserodolfofreitas
Created December 3, 2012 09:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joserodolfofreitas/4193893 to your computer and use it in GitHub Desktop.
Save joserodolfofreitas/4193893 to your computer and use it in GitHub Desktop.
testing jsf - changes
---
layout: reveal
title: "JavaOne 2012 | Testing JSF applications with Arquillian and Selenium"
---
%section
%img.splash{:src => "assets/jbosscommunity/splash_text.png"}
%section
%h1 Testing JSF applications
%h2.capitalize with Arquillian and Selenium
%ul.intro
%li Jose Rodolfo Freitas(@joserodolfo_f)
%li Brian Leathem(@bleathem)
%li JavaOne LAD 2012 - Sao Paulo
%li 2012-12-06
%section
%section
%h2 The plan
:textile
* Brief Review of _back-end_ testing
* How to test the _front-end_ with Selenium
* Beyond Selenium with Arquillian Extensions
%div
%img.clear{:src => "assets/images/storm_troopers.jpg", :alt => "http://www.flickr.com/photos/jdhancock/5845280258/"}
%section
%h2 Who are we?
%ul
%li R&D Software Engineer
%li JEE Consultant
%li OSS contributor and former JSFUnit community leader
%div
%img{:src => "assets/images/silhouette.png", :alt => "http://leslycorazon.wikispaces.com/file/detail/head-silhouette-with-question-mark.png/319199232"}
%section
%h2 Who are we?
%ul
%li Senior Software Engineer at Red Hat
%li Richfaces Project Leader
%div
%img{:src => "assets/images/silhouette.png", :alt => "http://leslycorazon.wikispaces.com/file/detail/head-silhouette-with-question-mark.png/319199232"}
%section
%h2 Who's behind all this?
%table.people{:style => "margin: auto"}
%tr
%td
%img{:src => "assets/images/aslak.jpg"}
%td
%ul
%li
Aslak Knutsen (
%a{:href => "https://twitter.com/aslakknutsen"}@aslakknutsen
)
%li Arquillian Lead
%tr
%td
%img{:src => "assets/images/ALR.jpg"}
%td
%ul
%li
Andrew Lee Rubinger (
%a{:href => "https://twitter.com/ALRubinger"}@ALRubinger
)
%li Shrinkwrap Lead
%tr
%td
%img{:src => "assets/images/lfryc.jpg"}
%td
%ul
%li
Lukáš Fryč (
%a{:href => "https://twitter.com/lfryc"}@lfryc
)
%li Arquillian Graphene Lead, Arquillian Warp Lead
%tr
%td
%img{:src => "assets/images/kpiwko.jpg"}
%td
%ul
%li
Karel Piwko (
%a{:href => "https://twitter.com/karelpiwko"}@karelpiwko
)
%li Arquillian Drone Lead
%tr
%td
%img{:src => "assets/images/jhuska.jpg"}
%td
%ul
%li
Juraj Huska (
%a{:href => "https://twitter.com/j_huska"}@j_huska
)
%li Arquillian Graphene, Arquillian Drone
%tr
%td
%img{:src => "assets/images/jan_papousek.jpg"}
%td
%ul
%li
Jan Papoušek (
%a{:href => "https://twitter.com/jan_papousek"}@jan_papousek
)
%li Arquillian Graphene, Arquillian Drone
%section
%h2 Who are you?
%div
%img.plain{:src => "assets/images/crowd.png"}
.notes
Can I see a show of hands for those...
%ul
%li Who currently develop web applications with JSF?
%li Who are using Arquillian to test their application logic?
%li Who are using Selenium to test their application front-end?
%section
%section
%h2 Back-end testing
%div.relative.inline{:style => "top: 50px"}
%img.plain{:src => "assets/images/check.png"}
%p A well understood problem
%section
%h2 Back-end testing | Unit tests
%div.relative.inline{:style => "top: 100px"}
%img.plain.nomargin{:src => "assets/images/masks.png"}
%div
%h4 JUnit / TestNG
%ul.bullets.fixed-width-wide
%li Easy to test self-contained logic
%li Mocks allow us to extend unit tests further
%section
%h2 Back-end testing | "Real" tests
%div.left.relative.inline{:style => "top: 50px"}
%img.plain{:src => "assets/images/testing-tags.png"}
%div
%h4 "Real tests"
%ul.bullets
%li Test code in-container
%li No "surprises" in production
%section
%h2 Enter Arquillian
%div.inline
%div
%img.plain{:src => "assets/images/arquillian_ike.png"}
%h4 Don't mock me!
%ul.bullets
%li Bring the container to the test
%li Micro-deployments
%section
%section
%h2 Example | GreeterTest
%div.relative.inline{:style => "top: 100px"}
%div
%h1{:style => "font-size: 120pt; margin-right: 30px"} Hello <br /> World!
%ul.bullets
%li Basic Arquillian test
%li
Demonstrate
%span.monospace @Inject
%li Introduce Shrinkwrap
%section
%h2 GreeterTest | The Bean
%pre{:class => "prettyprint", :style => "font-size: 18px; top: 50px"}
%code{:id => "greeter"}
= preserve do
:escaped
public class Greeter {
private PhraseBuilder phraseBuilder;
@Inject
public Greeter(PhraseBuilder phraseBuilder) {
this.phraseBuilder = phraseBuilder;
}
public void greet(PrintStream to, String name) {
to.println(createGreeting(name));
}
public String createGreeting(String name) {
return phraseBuilder.buildPhrase("hello", name);
}
}
%section
%h2 GreeterTest | Injection
%pre{:class => "prettyprint", :style => "font-size: 18px; top: 100px"}
%code{:id => "phraseBuilder"}
= preserve do
:escaped
public class PhraseBuilder {
private Map<String, String> templates;
public String buildPhrase(String id, Object... args) {
return MessageFormat.format(templates.get(id), args);
}
@PostConstruct
public void initialize() {
templates = new HashMap<String, String>();
templates.put("hello", "Hello, {0}!");
}
}
%section
%h2 GreeterTest | Test
%pre{:class => "prettyprint", :style => "font-size: 15px; top: 50px"}
%code{:id => "basicTest"}
= preserve do
:escaped
@RunWith(Arquillian.class)
public class GreeterTest {
@Deployment
public static JavaArchive createDeployment() {
JavaArchive jar = ShrinkWrap.create(JavaArchive.class)
.addClasses(Greeter.class, PhraseBuilder.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
return jar;
}
@Inject
Greeter greeter;
@Test
public void should_create_greeting() {
Assert.assertEquals("Hello, Earthling!",
greeter.createGreeting("Earthling"));
}
}
.fragment.screen{:data=>{:prettify=>"basicTest", :prettify_lines=>"0:1"}}
.fragment.screen{:data=>{:prettify=>"basicTest", :prettify_lines=>"2:9"}}
.fragment.screen{:data=>{:prettify=>"basicTest", :prettify_lines=>"10:11"}}
.fragment.screen{:data=>{:prettify=>"basicTest", :prettify_lines=>"13:17"}}
%section
%h2 The Magic
%ul.relative{:style => "top: 50px"}
%li
POM Dependencies
%ul
%li Arquillian
%li JUnit | TestNG
%li Deployment Container
%li &nbsp;
%li
Arquillian.xml
%ul
%li Configuration of the test container
%section
%h2 GreeterTest | POM
%pre{:class => "prettyprint", :style => "font-size: 17px; top: 50px"}
%code{:id => "basicTest"}
= preserve do
:escaped
<profile>
<id>arquillian-jbossas-managed</id>
<dependencies>
...
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-arquillian-container-managed</artifactId>
<version>7.1.1.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.protocol</groupId>
<artifactId>arquillian-protocol-servlet</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
%section
%h1.relative.demo Demo Time
%section
%section
%h2 Front-end Testing
%img.plain{:src => "assets/images/user.png"}
%section
%h2 Ideal: Pyramid of test coverage
%div
%img{:src => "assets/images/idealautomatedtestingpyramid.png", :alt => "http://watirmelon.files.wordpress.com/2012/01/idealautomatedtestingpyramid.png", :style => "clear: both"}
%section
%h2 Reality: Ice-cream Cone of test coverage
%div
%img{:src => "assets/images/softwaretestingicecreamconeantipattern.png", :alt => "http://watirmelon.files.wordpress.com/2012/01/softwaretestingicecreamconeantipattern.png", :style => "clear: both"}
%section
%h2 Howto increase automated test coverage?
%ul
%li &nbsp;
%li
Unit tests of GUI code
%span{:style => "margin-left: 20px; font-weight: bold"} -- lots of mocks!
%li &nbsp;
%li Let's focus on real-tests
%section
%h2 Real tests &rarr; real browsers
.images.white.relative{:style => "top: 100px"}
%table
%tr
%td
%img.relative{:src => "assets/images/browsers/firefox.png", :style => "left: 50px"}
%td
%img.relative{:src => "assets/images/browsers/chrome.png", :style => "top: -50px"}
%td
%img.relative{:src => "assets/images/browsers/ie.png", :style => "left: -50px"}
%tr
%td
%img.relative{:src => "assets/images/browsers/opera.png"}
%td
%img.relative{:src => "assets/images/browsers/safari.jpg"}
%td
%img.relative{:src => "assets/images/browsers/konqueror.png"}
%tr
%td
%img.relative{:src => "assets/images/browsers/epiphany.png", :style => "left: 50px"}
%td
%img.relative{:src => "assets/images/browsers/camino.png", :style => "top: 50px"}
%td
%img.relative{:src => "assets/images/browsers/seamonkey.png", :style => "left: -50px"}
%section
%section
%h2 Selenium
%br
%h5.bold
Selenium automates browsers.
%span{:style => "font-style: italic"} That's it.
%div.relative.inline{:style => "top: -20px"}
%img.plain.nomargin{:src => "assets/images/selenium.png"}
%div
%p.left.block WebDriver API:
%ul.bullets{:style => "width: 400px"}
%li Unified API for all browsers
%li Headless tests via HtmlUnit
%section
%h2 Selenium | API
%br
%div.left
%p WebDriver
%hr
%p.italic Represents the web browser
%hr
%ul
%li
Methods:
%ul
%li .get()
%li .findElement(By)
%section
%h2 Selenium | API
%br
%div.left
%p WebElement
%hr
%p.italic Represents an HTML element.
%hr
%ul
%li
Methods:
%ul
%li .click()
%li .sendKeys()
%li .getText()
%li .findElement(By)
%section
%h2 WebElement Lookup
%br
%p.left Selenium/WebDriver programmatic API:
%pre{:class => "prettyprint", :style => "font-size:25px"}
%code
= preserve do
:escaped
WebElement username
= driver.findElement(By.id("username"))
%br
%p.left Alternative @FindBy annotation:
%pre{:class => "prettyprint", :style => "font-size:25px"}
%code
= preserve do
:escaped
@FindBy(id="username")
WebElement username;
...
PageFactory.initElements(driver, this);
.notes
%ul
%li By.ByClassName, By.ByCssSelector, By.ById, By.ByLinkText, By.ByName, By.ByPartialLinkText, By.ByTagName, By.ByXPath, ByChained, ByIdOrName
%section
%h2 Selenium IDE - Easy!
%div.relative.inline{:style => "top: 30px"}
%img.plain.nomargin{:src => "assets/images/selenium-ide.png"}
%ul.bullets
%li Easy to write/record tests
%li Replay tests against all browsers
%section
%section
%h2 Example | Selenium
%img.plain{:src => "assets/images/kitchensink.png", :style => "height: 500px"}
%section
%div{:style => "vertical-align: top; text-align: left"}
%h2.left{:style => "display: inline-block; vertical-align: top; text-align: center"}
Selenium
%br
IDE
%img.relative{:src => "assets/images/selenium-ide-test.png", :style => " display: inline-block; vertical-align: top; margin: 0;"}
%section
%h2 Selenium Ex. | The Test
%pre{:class => "prettyprint", :style => "font-size: 10px"}
%code{:id => "selenium"}
= preserve do
:escaped
public class SeleniumTest {
private WebDriver driver;
private String baseUrl;
private StringBuffer verificationErrors = new StringBuffer();
@Before
public void setUp() throws Exception {
driver = new FirefoxDriver();
baseUrl = "http://kitchensink-richfaces.rhcloud.com/";
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
@Test
public void test() throws Exception {
driver.get(baseUrl + "/");
driver.findElement(By.id("reg:memberForm:name")).clear();
driver.findElement(By.id("reg:memberForm:name")).sendKeys("Brian Leathem");
driver.findElement(By.id("reg:memberForm:email")).clear();
driver.findElement(By.id("reg:memberForm:email")).sendKeys("bleathem@test.ca");
driver.findElement(By.id("reg:memberForm:phoneNumber")).clear();
driver.findElement(By.id("reg:memberForm:phoneNumber")).sendKeys("234234234235");
driver.findElement(By.id("reg:register")).click();
Thread.sleep(2000);
try {
assertEquals("Brian Leathem", driver
.findElement(By.id("reg:memberTable:0:member_name")).getText());
} catch (Error e) {
verificationErrors.append(e.toString());
}
}
@After
public void tearDown() throws Exception {
driver.quit();
String verificationErrorString = verificationErrors.toString();
if (!"".equals(verificationErrorString)) {
fail(verificationErrorString);
}
}
}
.fragment.screen{:data=>{:prettify=>"selenium", :prettify_lines=>"5:11"}}
.fragment.screen{:data=>{:prettify=>"selenium", :prettify_lines=>"12:29"}, :style => "top: -40px; margin: 0 -90px"}
.fragment.screen{:data=>{:prettify=>"selenium", :prettify_lines=>"31:38"}, :style => "top: -30px;"}
%section
%h1.relative.demo Demo Time
%section
%h2 Selenium - Problems!
%div.relative{:style => "top: 50px"}
%p Not enough abstraction, Highly repetitive
%p &rarr; Maintenance problem
%img.plain{:src => "assets/images/frustrated.jpg", :alt => "http://www.flickr.com/photos/cellardoorfilms/7620375702/", :style => "margin-right: 4px"}
.notes
%ul
%li Tests focus against state transitions
%li Test only DOM/CSS expressed directly in the test
%section
%section
%h2 Automating Client-side testing
%div.relative{:style => "top: 30px"}
%ul
%li Deploy the app, run the tests!
%div
%img.plain{:src => "assets/images/assembly_line.jpg", :alt => "http://www.flickr.com/photos/pasukaru76/6893926948/"}
%section
%h2 Container lifecycle
%div.relative.inline{:style => "top: 40px"}
%div
%img.plain.nomargin{:src => "assets/images/arquillian_success.png"}
%h4 Ike has us covered!
%ul.bullets.fixed-width-narrow
%li Startup/shutdown the container
%li Create the testable archive
%li Deploy the application
%section
%h2 Client lifecycle?
%div.relative.inline{:style => "top: 40px"}
%img.plain.nomargin{:src => "assets/images/browser.png"}
%ul{:style => "width: auto"}
%li Start / Stop the Browser
%section
%h2 Arquillian Extensions
%dl.fragment
%dt Arquillian Drone
%dd Brings the browser to the test
%dl.fragment
%dt Arquillian Graphene
%dd Selenium integration and extension
%dl.fragment
%dt Arquillian Warp
%dd Testing on both sides of the request
%section
%section
%h2 Arquillian Drone
%div.relative.inline.left{:style => "top : 80px"}
%img.plain{:src => "assets/images/drone.png"}
%p{:style => "width: 550px"} Manages the browser life-cycle
%section
%h2 Arquillian Drone
%ul
%li
Integrates with:
%ul
%li Selenium/Webdriver
%li Arquillian Graphene
%pre.relative{:class => "prettyprint", :style => "font-size: 20px; top: 50px"}
%code
= preserve do
:escaped
@Drone
WebDriver browser
%br
%pre.relative{:class => "prettyprint", :style => "font-size: 20px; top: 50px"}
%code
= preserve do
:escaped
<extension qualifier="webdriver">
<property name="browserCapabilities">chrome</property>
</extension>
%section
%h2 Separation of concerns
%div.inline.left
%div
%p.block Devs focus: Author tests
%img{:src => "assets/images/build.jpg", :style => "width:400px; margin-top: 0", :alt => "http://www.flickr.com/photos/metrolibraryarchive/3267555668/"}
%div
%img{:src => "assets/images/inspection.jpg", :style => "width:400px", :alt => "http://www.flickr.com/photos/metrolibraryarchive/3267249336/"}
%p.block QA focus: Automate tests
%section
%section
%h2 Example | Google Drone Test
%div.relative.inline{:style => "top: 50px"}
%img{:src => "assets/images/google.png"}
%ul.bullets
%li Simple Drone test
%li
Demonstrate
%span.monospace @Drone
%section
%h2 Google Drone Test | Test
%pre.relative{:class => "prettyprint", :style => "font-size: 20px; top: 80px"}
%code{:id => "googleTest"}
= preserve do
:escaped
@RunWith(Arquillian.class)
public class GoogleDroneTest {
@Drone
WebDriver browser;
@Test
public void testOpeningHomePage() {
browser.get("http://www.google.com/");
List<WebElement> elements = browser.findElements(
By.xpath("//span[contains(text(), 'Google Search')]"));
Assert.assertTrue("Page not loaded", elements.size() > 0);
}
}
.fragment.screen{:data=>{:prettify=>"googleTest", :prettify_lines=>"0:1"}}
.fragment.screen{:data=>{:prettify=>"googleTest", :prettify_lines=>"3:4"}}
.fragment.screen{:data=>{:prettify=>"googleTest", :prettify_lines=>"6:13"}}
%section
%h1.relative.demo Demo Time
%section
%section
%h2 Arquillian Graphene
%div.relative.inline{:style => "top : 50px"}
%img.plain{:src => "assets/images/graphene-logo.png", :alt => "http://www.flickr.com/photos/core-materials/5057399792/"}
%p
Selenium
%span{:style => "font-variant : small-caps; font-style: italic; font-weight: bold"} Arquillian-Style
.notes
%ul
%li Arquillian integration starts with Drone and the instantiation of the WebDriver instance.
%li Graphene then takes new instance of WebDriver's browser session and store is in its Thread Local Context.
%li Graphene also wraps the WebDriver instance in order to intercept calls.
%section
%h2 Graphene 1.0
%dl
%dt GrapheneSelenium class
%dd type-safe Selenium class for Selenium 1.x
%pre.relative{:class => "prettyprint", :style => "top: 100px; font-size:25px"}
%code
= preserve do
:escaped
@Drone
GrapheneSelenium browser;
%canvas.fragment.screen.canvas
%section
%h2 Graphene 2.0
%div.relative.inline{:style => "top : 50px"}
%img.plain{:src => "assets/images/needle.png"}
%ul
%li More Injection
%li More Abstractions
%li More Extensions
%section
%section
%h2 Page Abstractions
%div.inline
%img.plain{:src => "assets/images/html.png"}
%p.fixed-width Encapsulate DOM elements behind a custom API
%section
%h2 Selenium | Page objects
%div.relative.inline{:style => "top : 50px"}
%img{:src => "assets/images/page.png"}
%div
%ul.fixed-width
%li A class that encapsulates the behaviour of a page
%section
%h2 Selenium | LoginPage
%pre{:class => "prettyprint", :style => "font-size: 20px; top: 30px"}
%code
= preserve do
:escaped
public class LoginPage {
private final WebDriver driver;
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public HomePage loginAs(String username, String password) {
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("passwd")).sendKeys(password);
driver.findElement(By.id("login")).submit();
return new HomePage(driver);
}
}
%section
%h2 Selenium | Page Instantiation
%p.relative{:style => "top: 100px"} Selenium PageFactory class instantiates Page Objects:
%pre.relative{:class => "prettyprint", :style => "font-size: 22px; top: 200px"}
%code
= preserve do
:escaped
LoginPage loginPage =
PageFactory.initElements(driver, LoginPage.class);
%section
%h2 Graphene | Page Injection
%p.relative{:style => "top: 100px"}
Graphene test enrichment manages the PageFactory
%div.relative{:style => "top: 150px"}
%pre{:class => "prettyprint", :style => "font-size: 22px;"}
%code
= preserve do
:escaped
@Page
LoginPage loginPage
%pre{:class => "prettyprint", :style => "font-size: 22px;"}
%code
= preserve do
:escaped
@FindBy(id="username")
WebElement username;
%section
%h2 Graphene | Page Fragments
%div.relative.inline{:style => "top : 50px"}
%img.nomargin{:src => "assets/images/page-fragment.png"}
%div
%ul.bullets.fixed-width-narrow
%li Page Objects ++
%li Encapsulate the behaviour of a page fragment
%br
%br
%div.relative{:style => "left: 20px"}
%pre{:class => "prettyprint", :style => "font-size: 20px; width: 525px; margin-right: 0;"}
%code
= preserve do
:escaped
@FindBy(css=".calendar")
CalendarFragment calendar;
%section
%h2 Abstraction FTW!
%p.italic.relative{:style => "top: 150px"}
Single place to update WebDriver code
%br
when the underlying DOM changes
%section
%section
%h2 Waiting...
%p.left Simplest case:
%pre{:class => "prettyprint", :style => "font-size: 20px;"}
%code
= preserve do
:escaped
Thread.sleep(2000);
%p.left Selenium provides the WebDriverWait class
%pre{:class => "prettyprint", :style => "font-size: 20px;"}
%code
= preserve do
:escaped
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
.until(new ExpectedCondition<WebElement>(){
@Override
public WebElement apply(WebDriver d) {
return d.findElement(By.id("myDynamicElement"));
}});
%br
%p.left More boiler plate!
%section
%h2 Graphene wait helpers
%table.left.relative{:style => "width: 100%; top: 50px"}
%tr.bold
%th Helper
%th Description
%th.center Timeout
%tr
%td waitGui()
%td
waits for a short time
%br
eg. wait for client-side operations
%td.center 1
%tr
%td waitAjax()
%td
waits for longer time
%br
eg. wait for simple ajax request
%td.center 2
%tr
%td waitModel()
%td
waits for a long time
%br
eg. wait for database requests
%td.center 5
%section
%h2 Wait helper Usage
%pre.relative{:class => "prettyprint", :style => "font-size: 20px; width: 850px; top: 100px"}
%code
= preserve do
:escaped
By id = By.id("button");
WebElement element = driver.findElement(id);
...
waitModel(element(id).isVisible));
...
waitModel(element(element).not().textContains("blahblah"));
...
waitModel(attribute(id, "value").valueContains("blahblah"));
...
waitModel(attribute(element, "value").not().valueEquals("blahblah"));
%section
%h2 Request guards
%div.inline
%img{:src => "assets/images/beefeater.jpg",:alt => "http://www.flickr.com/photos/abennett96/2717629123/"}
%div{:style => "width: 570px"}
%p.left{:style => "font-style: italic"} Blocks the Selenium test execution until a network communication caused by a given action ends
%dl
%dt guardHttp(button).click();
%dd blocks on HTTP
%dt guardXhr(button).click();
%dd blocks on XHR (Ajax)
%section
%h2 Request guard | Usage
%pre{:class => "prettyprint", :style => "font-size: 20px; top: 30px"}
%code
= preserve do
:escaped
@RunWih(Arquillian.class)
public class TestClass {
 
    @FindBy(id="http")
    private WebElement httpButton;
 
    @FindBy(id="xhr")
    private WebElement xhrButton;
 
    @Test
    public void testSimple() {
        guardHttp(httpButton).click();
        guardXhr(xhrButton).click();
    }
}
%section
%section
%h2 Javascript Interfaces
%div.relative{:style => "top: 40px"}
%p{:style => "font-style: italic"} Execute JavaScript from your Java test
%pre.relative{:class => "prettyprint", :style => "font-size: 24px; top: 80px"}
%code
= preserve do
:escaped
@JavaScript("javascriptObject")
@Dependency(sources = {"file.js"})
public interface Background {
void voidMethod(String color);
String someFunction();
}
%section
%h2 Example | Javascript Interface
%div.relative.inline{:style => "top: 50px"}
%img{:src => "assets/images/google.png"}
%ul.bullets{:style => "width: 410px"}
%li Manipulate the DOM
%li Bi-directional communication
%section
%h2 Example | Javascript Interface
%p.left
BackGround.java:
%pre{:class => "prettyprint", :style => "font-size: 20px"}
%code
= preserve do
:escaped
@JavaScript("myBackground")
@Dependency(sources = {"background.js"})
public interface Background {
void setBackground(String color);
String getBackground();
}
%p.left
background.js:
%pre{:class => "prettyprint", :style => "font-size: 20px"}
%code
= preserve do
:escaped
myBackground = {
setBackground : function (color) {
document.body.style.background = color;
},
getBackground : function () {
return document.body.style.background;
}
}
%section
%h2 Example | Javascript Interface
%pre{:class => "prettyprint", :style => "font-size: 17px"}
%code
= preserve do
:escaped
@RunWith(Arquillian.class)
public class JavaScriptTest {
@Drone
WebDriver browser;
@Test
public void testOpeningHomePage() throws Exception {
browser.get("http://www.google.com/");
Background background = JSInterfaceFactory.create(Background.class);
System.out.println(String.format(
"Background color is: %s", background.getBackground()));
background.setBackground("red");
System.out.println(String.format(
"Background color is: %s", background.getBackground()));
Thread.sleep((2000));
background.setBackground("");
System.out.println(String.format(
"Background color is: %s", background.getBackground()));
Thread.sleep((2000));
}
}
%section
%h1.relative.demo Demo Time
%section
%h2 Page Extensions
%p.left Automatically include Javascript Interfaces in your tests
%br
%p.left Simply:
%ul.bullets
%li Implement an interface
%li Register the extension
%br
%br
%p.left See the docs...
%a{:href => "https://docs.jboss.org/author/display/ARQGRA2/Page+Extensions", :style => "font-size: 30px"}
https://docs.jboss.org/author/display/ARQGRA2/Page+Extensions
%section
%h2 Request Guards | Impl
%pre{:class => "prettyprint", :style => "font-size: 20px"}
%code
= preserve do
:escaped
@JavaScript(value = "Graphene.Page.RequestGuard")
@Dependency(sources = "Graphene.Page.RequestGuard.js",
interfaces=XhrInterception.class)
public interface RequestGuard extends InstallableJavaScript {
/**
* @return the last request type
*/
RequestType getRequestDone();
/**
* Clears the request type cache and returns the last
* request type
* @return the last request type
*/
RequestType clearRequestDone();
}
%section
%section
%h2 Interceptors with Graphene
%p.left.relative{:style => "top: 50px"} Work is in progress to port the Graphene 1 interceptor API to Graphene 2
%div.relative{:style => "top: 100px"}
%ul.bullets
%li Code executed around each WebDriver invocation
%li Cross-cutting concerns
%br
%br
%a{:href => "https://issues.jboss.org/browse/ARQGRA-79", :style => "font-size: 30px"}
https://issues.jboss.org/browse/ARQGRA-79
.notes
%ul
%li execute code before/after the selenium command will be performed
%li catch exceptions thrown when performing selenium command
%section
%section
%h2 Example | LoginScreen
%div.relative.inline{:style => "top: 50px"}
%img{:src => "assets/images/login_screen.png"}
%ul.bullets{:style => "width: 420px"}
%li Graphene Test
%li
Demonstrate Page Fragments
%section
%h2 LoginScreen | Deployment
%pre{:class => "prettyprint", :style => "font-size: 14px"}
%code{:id => "deployment"}
= preserve do
:escaped
public class Deployments {
public static final String WEBAPP_SRC = "src/main/webapp";
public static WebArchive getLoginScreenDeployment() {
return ShrinkWrap.create(WebArchive.class, "login.war")
.addClasses(Credentials.class, User.class, LoginController.class)
.addAsWebResource(new File(WEBAPP_SRC, "resources/bootstrap/css/bootstrap.css"),
"resources/bootstrap/css/bootstrap.css")
.addAsWebResource(new File(WEBAPP_SRC, "login.xhtml"))
.addAsWebResource(new File(WEBAPP_SRC, "home.xhtml"))
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addAsWebInfResource(
new StringAsset("<faces-config version=\"2.0\"/>"),
"faces-config.xml");
}
}
%section
%h2 LoginScreen | GrapheneTest
%pre{:class => "prettyprint", :style => "font-size: 10px"}
%code{:id => "graphene"}
= preserve do
:escaped
@RunWith(Arquillian.class)
public class LoginScreenGrapheneTest {
@Deployment(testable = false)
public static WebArchive createDeployment() {
return Deployments.getLoginScreenDeployment();
}
@Drone
WebDriver browser;
@ArquillianResource
URL contextPath;
@FindBy(id="loginForm:username")
private WebElement usernameInput;
@FindBy(id="loginForm:password")
private WebElement passwordInput;
@FindBy(id="loginForm:login")
private WebElement loginButton;
@Test
@RunAsClient
public void should_login_successfully() throws Exception {
String page = contextPath + "login.jsf";
browser.get(page);
usernameInput.sendKeys("demo");
passwordInput.sendKeys("demo");
loginButton.click();
Assert.assertTrue("User should be logged in!",
browser.findElements(By.xpath("//li[contains(text(), 'Welcome')]")).size() > 0);
}
}
.fragment.screen{:data=>{:prettify=>"graphene", :prettify_lines=>"0:1"}}
.fragment.screen{:data=>{:prettify=>"graphene", :prettify_lines=>"3:6"}}
.fragment.screen{:data=>{:prettify=>"graphene", :prettify_lines=>"8:21"}}
.fragment.screen{:data=>{:prettify=>"graphene", :prettify_lines=>"23:36"}, :style => "top: -20px; margin: 0 -100px; font-size: 18px"}
%section
%h2 LoginScreen | LoginFragment
%pre{:class => "prettyprint", :style => "font-size: 10px"}
%code{:id => "loginFragment"}
= preserve do
:escaped
public class LoginFragment {
@FindBy(id="loginForm:username")
private WebElement usernameInput;
@FindBy(id="loginForm:password")
private WebElement passwordInput;
@FindBy(id="loginForm:login")
private WebElement loginButton;
public void setUsername(Object username) {
usernameInput.sendKeys(username.toString());
}
public void setPassword(Object password) {
passwordInput.sendKeys(password.toString());
}
public void click() {
loginButton.click();
}
}
.fragment.screen{:data=>{:prettify=>"loginFragment", :prettify_lines=>"2:9"}}
.fragment.screen{:data=>{:prettify=>"loginFragment", :prettify_lines=>"11:21"}}
%section
%h2 LoginScreen | FragmentTest
%pre{:class => "prettyprint", :style => "font-size: 10px"}
%code{:id => "fragmentTest"}
= preserve do
:escaped
@RunWith(Arquillian.class)
public class LoginScreenFragmentTest {
@Deployment(testable = false)
public static WebArchive createDeployment() {
return Deployments.getLoginScreenDeployment();
}
@Drone
WebDriver browser;
@ArquillianResource
URL contextPath;
@FindBy(id="loginForm")
LoginFragment loginForm;
@Test
@RunAsClient
public void should_login_successfully() {
String page = contextPath + "login.jsf";
browser.get(page);
loginForm.setUsername("demo");
loginForm.setPassword("demo");
loginForm.click();
Assert.assertTrue("User should be logged in!",
browser.findElements(By.xpath("//li[contains(text(), 'Welcome')]")).size() > 0);
}
}
.fragment.screen{:data=>{:prettify=>"fragmentTest", :prettify_lines=>"14:15"}}
.fragment.screen{:data=>{:prettify=>"fragmentTest", :prettify_lines=>"17:29"}, :style => "top: 0px; margin: 0 -100px; font-size: 18px"}
%section
%section
%h2 Arquillian Warp
%div.relative.inline{:style => "top: 50px"}
%img.plain.nomargin{:src => "assets/images/warp.png"}
%ul.bullets.fixed-width-narrow
%li Testing on both sides of the request
%li En-rich the client request with a test to run on the server
%li En-rich the server response with results of the test
%section
%h2 JSFUnit | the old way
%div.relative.inline{:style => "top: 20px"}
%img.plain{:src => "assets/images/jsfunit.png"}
%ul.bullets.fixed-width
%li HtmlUnit - no real browsers
%li Assert state only at the end of the JSF lifecycle
%li JSF Only (No CDI, EJB etc.)
%section
%h2 Warp | the new way
%div.relative.inline{:style => "top: 20px"}
%img.plain{:src => "assets/images/new.png"}
%ul.fixed-width
%li
Advantages:
%ul
%li Selenium / any HTTP client
%li Test the entire JSF lifecycle
%li
Test any injectable resource
%ul
%li CDI
%li EJB
%li Any framework!
.notes
%ul
%li Not just JSF
%section
%section
%h2 Warp | Example
%pre{:class => "prettyprint", :style => "font-size: 8px"}
%code{:id => "warp"}
= preserve do
:escaped
@WarpTest
@RunWith(Arquillian.class)
public class LoginScreenWarpTest {
@Deployment
public static WebArchive createDeployment() {
WebArchive webArchive = Deployments.getLoginScreenDeployment();
webArchive.delete("WEB-INF/beans.xml");
webArchive.addAsWebInfResource(new File("src/test/resources/beans.xml"));
return webArchive;
}
@Drone
WebDriver browser;
@ArquillianResource
URL contextPath;
@FindBy(id="loginForm")
LoginFragment loginForm;
@Test
@RunAsClient
public void should_login_successfully() {
String page = contextPath + "login.jsf";
browser.get(page);
Warp.filter(new JsfRequestFilter()).execute(new ClientAction() {
@Override
public void action() {
loginForm.setUsername("demo");
loginForm.setPassword("demo");
loginForm.click();
}
}).verify(new CheckUsername());
Assert.assertTrue("User should be logged in!",
browser.findElements(By.xpath("//li[contains(text(), 'Welcome')]"))
.size() > 0);
}
public static class CheckUsername extends ServerAssertion {
@Inject
Credentials credentials;
@BeforePhase(Phase.UPDATE_MODEL_VALUES)
public void beforeUpdateModelValues() {
Assert.assertNull(credentials.getUsername());
}
@AfterPhase(Phase.UPDATE_MODEL_VALUES)
public void afterUpdateModelValues() {
Assert.assertEquals("demo", credentials.getUsername());
}
}
}
.fragment.screen{:data=>{:prettify=>"warp", :prettify_lines=>"0:2"}}
.fragment.screen{:data=>{:prettify=>"warp", :prettify_lines=>"4:10"}}
.fragment.screen{:data=>{:prettify=>"warp", :prettify_lines=>"12:19"}}
.fragment.screen{:data=>{:prettify=>"warp", :prettify_lines=>"21:39"}, :style => "top: -120px"}
.fragment.screen{:data=>{:prettify=>"warp", :prettify_lines=>"41:53"}, :style => "top: -180px"}
%section
%h1.relative.demo Demo Time
%section
%h2 Reducing test development turnaround
%p.left Best practices
%ul
%li.fragment Use a Remote Container
%li.fragment Re-usable Browser Session
%li.fragment
Use Shrinkwrap micro-deployments
%ul
%li Share deployments across tests
%li.fragment Arquillian jRebel extension
%section
%h2 The end?
%h3.fragment{:style => "margin-top: 160px"} The beginning - of JSF testing adventure with Arquillian and Selenium!
%section
%h2 Credits
%ul.links
%li
%a{:href => "http://www.flickr.com/photos/jdhancock/5845280258/"} http://www.flickr.com/photos/jdhancock/5845280258/
%a{:href => "http://leslycorazon.wikispaces.com/file/detail/head-silhouette-with-question-mark.png/319199232"} http://leslycorazon.wikispaces.com/file/detail/head-silhouette-with-question-mark.png/319199232
%a{:href => "http://watirmelon.files.wordpress.com/2012/01/idealautomatedtestingpyramid.png"} http://watirmelon.files.wordpress.com/2012/01/idealautomatedtestingpyramid.png
%a{:href => "http://watirmelon.files.wordpress.com/2012/01/softwaretestingicecreamconeantipattern.png"} http://watirmelon.files.wordpress.com/2012/01/softwaretestingicecreamconeantipattern.png
%a{:href => "http://www.flickr.com/photos/cellardoorfilms/7620375702/"} http://www.flickr.com/photos/cellardoorfilms/7620375702/
%a{:href => "http://www.flickr.com/photos/pasukaru76/6893926948/"} http://www.flickr.com/photos/pasukaru76/6893926948/
%a{:href => "http://www.flickr.com/photos/metrolibraryarchive/3267555668/"} http://www.flickr.com/photos/metrolibraryarchive/3267555668/
%a{:href => "http://www.flickr.com/photos/metrolibraryarchive/3267249336/"} http://www.flickr.com/photos/metrolibraryarchive/3267249336/
%a{:href => "http://www.flickr.com/photos/core-materials/5057399792/"} http://www.flickr.com/photos/core-materials/5057399792/
%a{:href => "http://www.flickr.com/photos/abennett96/2717629123/"} http://www.flickr.com/photos/abennett96/2717629123/
.links.commons
:plain
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/"><img alt="Creative Commons Licence" style="border-width:0" src="http://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">Testing JSF applications with Arquillian and Selenium</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="www.bleathem.ca/talks/2012-JavaOne/richfaces-mobile.html" property="cc:attributionName" rel="cc:attributionURL">Brian Leathem</a> is licensed under a <br /><a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License</a>.<br />Based on a work at <a xmlns:dct="http://purl.org/dc/terms/" href="https://github.com/bleathem/talks/tree/master/2012-JavaOne" rel="dct:source">github.com</a>.<br />Permissions beyond the scope of this license may be available at <a xmlns:cc="http://creativecommons.org/ns#" href="https://github.com/bleathem/talks/tree/master/2012-JavaOne" rel="cc:morePermissions">https://github.com/bleathem/talks/tree/master/2012-JavaOne</a>.
%section
%h2 Additional Resources
%ul.bullets
%li
Arquillian
%a{:href => "http://arquillian.org/guides/getting_started/"} Getting Started guides
%p.url
%a{:href => "http://arquillian.org/guides/getting_started/"} http://arquillian.org/guides/getting_started/
%li{:style => "margin-top: 1em"}
Arquillian Extensions:
%ul
%li
%a{:href => "https://docs.jboss.org/author/display/ARQ/Drone"} Drone docs
%p.url
%a{:href => "https://docs.jboss.org/author/display/ARQ/Drone"}https://docs.jboss.org/author/display/ARQ/Drone
%li
%a{:href => "https://docs.jboss.org/author/display/ARQGRA2/Getting+Started"} Graphene 2 docs
%p.url
%a{:href => "https://docs.jboss.org/author/display/ARQGRA2/Getting+Started"} https://docs.jboss.org/author/display/ARQGRA2/Getting+Started
%li
%a{:href => "https://docs.jboss.org/author/display/ARQ/Warp"} Warp docs
%p.url
%a{:href => "https://docs.jboss.org/author/display/ARQ/Warp"} https://docs.jboss.org/author/display/ARQ/Warp
%li{:style => "margin-top: 1em"}
%a{:href => "https://github.com/bleathem/TestingJSF"} Sample Code
%p.url
%a{:href => "https://github.com/bleathem/TestingJSF"} https://github.com/bleathem/TestingJSF
%section
%h2 Stay in the loop
%table.links.loop.relative{:style => "width: 100%; top: 80px"}
%tr
%td Project
%td
%a{:href => "http://arquillian.org"} arquillian.org
%td
%a{:href => "http://richfaces.org"} richfaces.org
%tr
%td Twitter:
%td
%a{:href => "https://twitter.com/#!arquillian"} @arquillian
%td
%a{:href => "https://twitter.com/#!richfaces"} @richfaces
%tr
%td Google+:
%td
%a{:href => "https://plus.google.com/100660127586085393031/"} +Arquillian
%td
%a{:href => "https://plus.google.com/b/115573055688754345457/"} +RichFaces
%tr
%td Forums:
%td
%a{:href => "https://community.jboss.org/en/arquillian?view=discussions"} Arquillian User forum
%td
%a{:href => "https://community.jboss.org/en/richfaces?view=discussions"} RichFaces User forum
%tr
%td IRC
%td #jbosstesting
%td #richfaces
%tr
%td Blog feed:
%td
%a{:href => "http://arquillian.org/blog/"} arquillian.org/blog/
%td
%a{:href => "http://planet.jboss.org/feed/richfacesall"} planet.jboss.org<br>/feed/richfacesall
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment