Google Analytics

Search

To search for specific articles you can use advanced Google features. Go to www.google.com and enter "site:darrellgrainger.blogspot.com" before your search terms, e.g.

site:darrellgrainger.blogspot.com CSS selectors

will search for "CSS selectors" but only on my site.


Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Thursday, September 25, 2014

Silencing ChromeDriver with WebDriver

While setting up a test environment today we decided to have the tests running on the same machine as the build radiator.

A build radiator takes up the entire display. It shows a green bar for each job on the build server. If someone checks in a change and it breaks a test, the bar turns red and everyone stops to fix the build.

The consequence of this is that the build radiator has to be visible to everyone in the room. Having a browser open on the display is not an option.

So we need to run our WebDriver tests without showing the browser or any other output. Our build server is running Linux. So we have WebDriver tests. We can run them from the command line using something like:
java org.testng.TestNG testng.xml
where testng.xml is a TestNG test suite example. When we run it as this we see the browser open and the tests executing. The tests were written using ChromeDriver. When we run this on the build radiator however, we don't want the browser opening. The solution is actually quite easy for Linux. We use an application called Xvfb:
xvfb-run --server-args="-screen 0 1600x1200x24" java org.testng.TestNG testng.xml
The command xvfb-run will run the application using the X Virtual FrameBuffer. The --server-args lets us pass arguments to the server. The "-screen 0" tells xvfb to use screen 0. The "1600x1200x24" tells xvfb to make the virtual display 1600 by 1200 with 24 bit depth. If your application has to work on 1024 by 768 and 16 bit colour then you can use "1024x768x16".

When you execute this you will not see the browser open. It almost seems like nothing is happening. The only thing you will see is the output from TestNG (a dot for a pass, an I for an ignore and an F for a failure) and the output from chromedriver. What if you want to look at the logs and see just the output from TestNG; not interlaced with output from chromedriver?

This requires a few changes to the creation of the WebDriver object. Normally, you might have something like:
System.setProperty("webdriver.chrome.driver", "./chromedriver");
WebDriver driver = new WebDriver();
but this outputs chromedriver log information to the screen. You could use:
System.setProperty("webdriver.chrome.driver", "./chromedriver");
System.setProperty("webdriver.chrome.args", "--disable-logging");
WebDriver driver = new WebDriver();
This will stop most the output but you will see the header for when chromedriver starts up:
Starting ChromeDriver (v2.9.248307) on port 9515
So how do you get rid of this? I was digging through the code for chromedriver (remember it is open source) and I found some code where it was checking for the property webdriver.chrome.silentOutput. If this was set to true then it would run with the silent flag set to true. So I tried:
System.setProperty("webdriver.chrome.driver", "./chromedriver");
System.setProperty("webdriver.chrome.args", "--disable-logging");
System.setProperty("webdriver.chrome.silentOutput", "true");
WebDriver driver = new WebDriver();
Sure enough that did it. Complete silence from chromedriver.

Friday, June 20, 2014

Desktop automation tools

Talking to one of my colleagues today. He asked me about testing desktop applications. As someone who predates the Internet, testing desktop applications was not foreign to me. However I realized that many testers today have only worked on web or mobile device applications.

Testing things like Microsoft Word, Notepad, Calendar or Eclipse isn't something many testers have done.

You wouldn't use something like Selenium, cucumber or Watir to test a desktop application.

So where would you go to find desktop application test tools? The first place I look is http://en.wikipedia.org/wiki/List_of_GUI_testing_tools. At this time this page notes:


  • AutoIt: http://www.autoitscript.com/site/autoit/ (free)
  • IBM/Rational Functional Tester: http://www-03.ibm.com/software/products/en/functional (commercial)
  • Quality First Software: http://www.qfs.de/en/index.html (commercial)
  • Sikuli: http://www.sikuli.org/ (free)
  • SilkTest: http://www.borland.com/products/silktest/ (commercial)
  • Test Automation FX: http://www.testautomationfx.com/ (commercial)
  • Telerik TestStudio: http://www.telerik.com/teststudio (commercial)
In addition to these links I would also check out:
  • http://www.ministryoftesting.com/resources/software-testing-tools/
  • http://sourceforge.net/directory/development/development-testing/freshness:recently-updated/
The first site has a wide range of links and it currently actively maintained. Unfortunately, it has more links for web and mobile testing but there are some desktop application tools listed.

The second link is to sourceforge. It will take you to the recently updated testing links. It includes open source applications for all sorts of testing. You can remove the "recently-updated" filter and see more but I usually restrict myself to recently updated tools. 

If the tool is for desktop testing and it has not been recently updated, there is a strong chance it does not work with modern applications or a current operating system. The older the tool the higher the risk it will not work on Windows 7 or 8, Mac OS X 10.8 or 10.9.

If you enter in a search term, like "testing" then a whole set of menus will appear below the search term and you can narrow things down even further. For example, after entering "testing" into the search text box I can select OS to be "Mac" and category "Quality Assurance".

I can also clear all the filters and search for things which might help narrow it down in different ways. For example, if I know how to program Java and I think I might want to add to the open source tool then I can filter for things with Programming Language "Java". Additionally, if the desktop application is written using Swing and Java I could search for Programming Language "Java", Category "Testing" then enter "swing". From the results I find jrobot (http://jayrobot.sourceforge.net/) and gtt (GUI Test Tool; http://gtt.sourceforge.net/). 

It is also worth just poking around sourceforge and keeping a mental note about things you find. This is because you'll be surprised what you stumble. For example, JFCUnit is a good tool for testing Java Swing applications but it didn't come with JFCUnit (http://jfcunit.sourceforge.net/).


Wednesday, April 30, 2014

WebDriverWait versus FluentWait


The WebDriver code waits for the page to load but today we have dynamic websites using things like angularjs and Ajax. WebDriver does not wait for the javascript to execute. So you might have something like:
<a href="http://{{env}}.company.com/foo/bar.html">{{env}}</a>
When the page loads, javascript runs and converts {{env}} to some defined value. So on the test environment {{env}} might convert to test, on stage it converts to stage and on production it converts to www. However, WebDriver will not wait for the javascript to make the substitution.  The end result is clicking the element will cause WebDriver to go to "{{env}}.company.com" when we really wanted it to wait and go to "www.company.com".

So how do we make WebDriver wait for the variable to be updated by javascript?

The answer is WebDriverWait or FluentWait. Below are examples of how you can use WebDriverWait and FluentWait to wait for the javascript to finish:
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.*;
import java.util.Arrays;
import static java.util.concurrent.TimeUnit.*;
import static org.junit.Assert.assertFalse;

public class TestingFluentWaitAndWebDriverWait {
  WebDriver driver;
  WebElement button;

  @Before
  public void setUp() {
    System.setProperty("webdriver.chrome.driver", "/Users/ThoughtWorks/IdeaProjects/SeleniumTesting/chromedriver2");
    DesiredCapabilities dc = DesiredCapabilities.chrome();
    dc.setCapability("driver.switches", Arrays.asList("--start-maximized"));
    driver = new ChromeDriver(dc);
    driver.manage().window().maximize();
    driver.get("http://www.google.com");
    driver.findElement(By.cssSelector("#gbqfq")).sendKeys("\"Darrell Grainger\"");
    button = driver.findElement(By.cssSelector("#gbqfb"));
  }

  @Test
  public void testWebDriverWait() {
    new WebDriverWait(driver, 3).until(ExpectedConditions.visibilityOf(button)).click();
    assertMyBlogLinkExists();
  }

  @Test
  public void testFluentWait() {
    new FluentWait<WebElement>(button).withTimeout(3, SECONDS)
        .pollingEvery(100, MILLISECONDS)
        .until(new Function<WebElement, Boolean>() {
          public Boolean apply(WebElement w) {
            return w.isDisplayed();
          }
        });
    button.click();
    assertMyBlogLinkExists();
  }

  @Test
  public void testFluentWaitPredicate() {
    new FluentWait<WebElement>(button).withTimeout(3, SECONDS)
        .pollingEvery(100, MILLISECONDS)
        .until(new Predicate<WebElement>() {
          public boolean apply(WebElement w) {
            return w.isDisplayed();
          }
        });
    button.click();
    assertMyBlogLinkExists();
  }

  private void assertMyBlogLinkExists() {
    final String linkText = "QA & Testing";
    try {
    new FluentWait<WebDriver>(driver).withTimeout(3, SECONDS)
        .pollingEvery(100, MILLISECONDS)
        .ignoring(NoSuchElementException.class)
        .until(new Function<WebDriver, Boolean>() {
          public Boolean apply(WebDriver d) {
            WebElement link = d.findElement(By.linkText(linkText));
            return link.isDisplayed();
          }
        });
    } catch(TimeoutException te) {
      assertFalse(String.format("Timeout waiting for link: '%s'", linkText), true);
    }
  }

  @After
  public void tearDown() {
    if(driver != null)
    driver.quit();
  }
}
All three tests will result in the same outcome.

Thursday, November 28, 2013

Opening two windows for one WebDriver

I'm currently testing a project with two applications. The first is an administrator tool for updating/modifying the customer site and the second is the actual customer site.

There are three ways to test a project like this. The first is creating one instance of WebDriver and access each application one at a time, e.g.

WebDriver driver = new ChromeDriver();
driver.get(adminToolURL);
// do some admin stuff
driver.get(customerSiteURL);
// do some site stuff

The second would be to create two instances of WebDriver. One to access the admin tool and the other to access the customer site, e.g.

WebDriver adminDriver = new ChromeDriver();
adminDriver.get(adminToolURL);
WebDriver driver = new InternetExplorerDriver();
driver.get(customerSiteURL);
// do some admin stuff with adminDriver
// do some site stuff with driver

The nice thing about this scenario is that you might have different browser requirements. The admin tool only has to work with one browser and the customer site has to work with a variety of browsers. So I can hard code the adminDriver to one browser and configure the test framework to change the browser. Run 1, InternetExplorerDriver; run 2, ChromeDriver; run 3, SafariDriver; etc.

The third way is to create one instance of WebDriver and open two windows. If you use the driver.get() method, it will open the site in the current window. To open a second window you'll need to use something in Selenium to force a second window. The trick is to use JavascriptExecutor to run javascript which opens a second window, e.g.

WebDriver driver = new ChromeDriver();
driver.get(adminToolURL);
Set<String> windows = driver.getWindowHandles();
String adminToolHandle = driver.getWindowHandle();
((JavascriptExecutor)driver).executeScript("window.open();");
Set<String> customerWindow = driver.getWindowHandles();
customerWindow.removeAll(windows);
String customerSiteHandle = ((String)customerWindow.toArray()[0]);
driver.switchTo().window(customerSiteHandle);
driver.get(customerSiteURL);
driver.switchTo().window(adminToolHandle);

The first two lines are straight forward and open a window with the admin tool. The next few lines does the following: get a list of the currently open windows, save the window handle for the admin tool window, open a new window using executeScript, get a second list of the currently open window (this will be the same as the first list PLUS the new window). Next remove all the original windows handles from the second list. This should leave the second list (customerWindow) with only one window handle, e.g. the new window. The last three lines show you how to switch between the customer site window and the admin tool window.


Thursday, October 31, 2013

WebDriverWait

In an older article I wrote about a method I created called waitForElement. After looking through some of the WebDriver code I found WebDriverWait. You should read the original article before you read this one.

The WebDriverWait class extends FluentWait<WebDriver> which is a special version of FluentWait which has WebDriver instances.

In the article for waitForElement I was talking about how Selenium cannot detect that Javascript has finished altering the DOM. So if you try to interact with an element with Selenium and Javascript is still altering it, you will have undefined results. The waitForElement method would wait for the element to be full rendered, i.e. for the Javascript to be finished. The WebDriverWait serves the same purpose.

To define a WebDriverWait would be the following:
WebDriverWait wdw = new WebDriverWait(driver, timeoutInSeconds, pollingTimeInMs);
The first parameter is an already existing WebDriver element. The second parameter is how long we want to wait for the element to be present. If we set it to say 30 it will wait for 30 seconds before it throws an error. The third parameter is how often you want to check for the element. If you set it really low, e.g. 10, it will check every 10 milliseconds. However this could put a load on the test machine. If you know it typically takes 55 milliseconds then maybe waiting for 60 would be best. You would not want to wait for 1000 milliseconds as it would make your test too slow.

This just creates the WebDriverWait object. It doesn't actually do the waiting. Let's say you click the ACCEPT checkbox. This causes Javascript to make the Next button visible. If you just click the ACCEPT checkbox then click the Next button it might fail because the Next button isn't visible as quickly as you can click it. So you need to click the ACCEPT checkbox, wait for the Next button to be visible then click the Next button. Here is some example code:
long timeoutInSeconds = 30;
long pollingTimeInMs = 250;
WebDriverWait wdw = new WebDriverWait(driver, timeoutInSeconds, pollingTimeInMs);
wdw.until(visibilityOfElementLocated(By.id("next"))).click();
This will check every 250 milliseconds to see if the element located by id='next' is visible. If the element does not become visible in 30 seconds it will fail the step and throw an error.

The list of conditions you can wait for are:
presenceOfElementLocated(by);
visibilityOf(driver.findElement(by));
alertIsPresent();
elementSelectionStateToBe(by, true);
elementSelectionStateToBe(we, true);
elementToBeClickable(by);
elementToBeSelected(by);
frameToBeAvailableAndSwitchToIt(frameLocator);
invisibilityOfElementLocated(by);
invisibilityOfElementWithText(by,text);
presenceOfAllElementsLocatedBy(by);
textToBePresentInElement(by, text);
textToBePresentInElementValue(by, text);
titleContains(title);
titleIs(title);
visibilityOf(we);
visibilityOfElementLocated(by);
The WebDriverWait also lets you alter the polling time, timeout, message displayed when it times out and exceptions it should ignore while waiting:
wdw.pollingEvery(delayBetweenPolling, TimeUnit.MILLISECONDS)
.ignoring(NoSuchElementException.class)
.withTimeout(timeoutInSeconds, TimeUnit.SECONDS)
.withMessage(message)
.until(visibilityOfElementLocated(By.id("next"))).click();
Because you can set the unit of measure for polling time and timeout, you can refine these after you instantiate the WebDriverWait object.

.

Thursday, August 29, 2013

Little discussed Java classes in WebDriver

Sleeper

Recently I have been exposed to classes in WebDriver that I did not know existed. I started with Selenium when it was beta and found myself inventing methods to do things for Selenium and later for WebDriver. So I never really looked at the extra capabilities for WebDriver. Even before Selenium I often needed to write code that slept for a few seconds. The proper way to use the Thread.sleep method would be:

    public void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch(InterruptedException ie) {
            throw new RuntimeException(ie);
        }
    }
Today I decided to scan through the WebDriver APIs. While I was doing this I found the Sleeper class. It has two APIs which are nice to use. Rather than using the method above you can use:
    sleepTight(2000);        // sleep for 2000 milliseconds
    sleepTightInSeconds(2);  // sleep for 2 seconds
SystemClock

Another helpful class I found was the SystemClock class. It implements the Clock interface and has three functions:

    SystemClock sc = new SystemClock();

    long current = sc.now();
    long early = sc.laterBy(-60000);
    long later = sc.laterBy(60000)
    System.out.println(sc.isNowBefore(later));  // true
    System.out.println(sc.isNowBefore(early));  // false
    System.out.println(sc.isNowBefore(now));    // false
The value of 'current' will be the timestamp of now. It is similar to using System.currentTimeMillis() and the API docs actually refer to System.currentTimeMillis(). Obviously, the command immediately after sc.now() will be greater than 'current' just because the call to sc.now() will take some time. The class also has the laterBy method. It talks about passing it a positive long but you can actually pass a negative number. In this case I passed in -60000. The value is in milliseconds. So this would be -60 seconds from 'now'. The third line sets later to 60 seconds later. A few milliseconds will have passed so if the time was 1377825553203 when I called sc.now() then sc.isNowBefore() get called and set 'now' to a few milliseconds later. Maybe 'now' will be 1377825554713. So sc.isNowBefore(later) will be true because later will be around 1377825553203 + 60000 and therefore later than 'now'. Where as sc.isNowBefore(early) will be false because 'now' will be after 1377825553203 - 60000. The odd one is sc.isNowBefore(current). This is because 'current' was now when it was set but a few lines later and 'now' will be after 'current', i.e. now > current, and sc.isNowBefore(current) will be false.

ThreadGuard

This class is more for people writing multi-threaded tests. It is rare for me to write multi-threaded tests. Hopefully you will never have to go there but if you do it is important to know that the implementations of WebDriver (ChromeDriver, FirefoxDriver, InternetExplorerDriver, etc.) are not guaranteed to be thread safe. To make them thread safe you can use the ThreadGuard class. It is actually pretty simple to use. If you have:

    WebDriver driver = new FirefoxDriver();
You can make it thread safe by changing it to:
    WebDriver driver - ThreadGuard.protect(new FirefoxDriver());
It is that simple. I won't get into why you are using multi-threaded tests. If you don't know if you are using multi-threading then you are probably not using multi-threading.

UrlChecker

UrlChecker is also a helpful class. It checks to see if a URL is returning an HTTP response of 200 OK. You could use it to check for a URL existing before you navigate to it or do a if URL does not exist then go to a different URL. For example,

    try {
        UrlChecker uc = new UrlChecker();
        String testURL = "http://localhost:8080/testapp/index.html";
        uc.waitUntilAvailable(5, TimeUnit.SECONDS, new URL(testURL));
        driver.get(testURL)
    } catch(UrlChecker.TimeoutException te) {
        // if the testURL does not become available in 5 seconds
        // this code will be run
    } catch(MalformedURLException mue) {
        // needed by URL class
        // if you think the testURL might be malformed handle it here
    }

Urls

This class has two helpful static methods:

    String testURL = "http://localhost:8080/~darrell/tests/#/";
    System.out.println(Urls.toProtocolHostAndPort(testURL));
    System.out.println(Urls.urlEncode(testURL));
The first println will print "http://localhost:8080". As URLs go, the /~darrell/tests/#/ is the context root. If you try going to "http://localhost:8080" it must have a context root. It will just assume you meant "/" for the context root. The second println will print "http%3A%2F%2Flocalhost%3A8080%2F%7Edarrell%2Ftests%2F%23%2F". All the symbols are converted to their ASCII hexidecimal value prefixed with a percent symbol. So for example, %3A is a :, %2F is a /, %7E is a ~ and %23 is the #. There are other interesting classes in WebDriver but these are the less used/discussed classes. Other classes like WindowsUtils have enough happening that they really need their own article. I will say that the following are some interesting classes which require more investigation:
  • FluentWait
  • Platform
  • ProcessUtils
  • Select
  • Wait
  • WindowsUtils

Tuesday, May 7, 2013

Is an element on the visible screen

A number of times I have been on a website where clicking a link goes to a specific page with a specific element at the top of the page. For example, I am reading an article and it quotes a book. The quote is actually from page 17, paragraph 2, first sentence. So when I click the link to the article it should take me to page 17, paragraph 2, first sentence.

So how would you automate testing this?

The simple answer would be to find the WebElement which is expected to be at the top of the page. This is the easy part. Then you need to see if the WebElement is on the visible screen. If you click it, WebDriver will scroll it into view. This is not what we want.

So what do we do? The answer is find out the size of the visible window then see if the bottom-right corner of the WebElement is inside that dimension. So here is the code for that:

  private boolean isWebElementVisible(WebElement w) {
    Dimension weD = w.getSize();
    Point weP = w.getLocation();
    Dimension d = driver.manage().window().getSize();

    int x = d.getWidth();
    int y = d.getHeight();
    int x2 = weD.getWidth() + weP.getX();
    int y2 = weD.getHeight() + weP.getY();

    return x2 <= x && y2 <= y;
  }
This will tell me of the element is on the visible window. If the element is not on the visible window it will return false. If the element is on the visible window it will return true... well almost. Unfortunately, you can get the size of the browser window but you really want the size of the chrome, inside the window. So if the window dimension is 1024x768 it is really telling you how big the OUTSIDE of the window is and not the inside. If the element is located at 1024x768 it is really off the visible window. For example, on my Google Chrome the tabs/title is 22px high. So the visible screen would really be 768 - 22 or 746px.

So this is a close approximation but there is a small chance of the element being just off the bottom of the window and still return true. If you are running driver.manage().window().maximize() the odds that something is going to be off the bottom of the visible window is small enough that it is not worth worrying about. Add to that we would hopefully be manually testing the area occasionally during exploratory testing and the risk should be acceptable.

There is another problems with this. We aren't actually checking that the upper-left corner of the element is at the top of the screen. You might think, why not just check that the element we are looking for is at position (0, 0)? The reason for not checking this is because the element might not be exactly at (0, 0). If the screen is large enough for 20 elements, the page only has 4 elements and the element we are looking for is at the second position, it will be impossible for the element we are looking for to scroll. So it will be below (0, 0) but still on the visible window.

A good example if this code would be:

    driver.get("http://www.w3schools.com");
    WebElement we = driver.findElement(By.cssSelector("#gsc-i-id1"));
    assertTrue(isWebElementVisible(we));
    WebElement we2 = driver.findElement(By.cssSelector("#footer"));
    assertFalse(isWebElementVisible(we2));

This will go to www.w3schools.com, find the Google search input at the top of the page, confirm it is visible. Then it will find the footer at the bottom of the page and confirm it is not visible.

Wednesday, July 4, 2012

Creating a screen capture on every action

Someone recently commented on an article I wrote about generating a screen capture when an exception is thrown (see Generating a screen capture on exception thrown with Selenium 2).

Performing an action when an exception is thrown is built into the Selenium framework. You just need to create the action to generate a screen capture and hook it into the framework.

The Selenium framework comes with a WebDriverEventListener interface. In my article above I created an implementation of the WebDriverEventListener interface. To do this properly, you need to implement the following methods:
beforeNavigateTo(String url, WebDriver driver);
afterNavigateTo(String url, WebDriver driver);
beforeNavigateBack(WebDriver driver);
afterNavigateBack(WebDriver driver);
beforeNavigateForward(WebDriver driver);
afterNavigateForward(WebDriver driver);
beforeFindBy(By by, WebElement element, WebDriver driver);
afterFindBy(By by, WebElement element, WebDriver driver);
beforeClickOn(WebElement element, WebDriver driver);
afterClickOn(WebElement element, WebDriver driver);
beforeChangeValueOf(WebElement element, WebDriver driver);
afterChangeValueOf(WebElement element, WebDriver driver);
beforeScript(String script, WebDriver driver);
afterScript(String script, WebDriver driver);
onException(Throwable throwable, WebDriver driver);
In my screen capture when an exception is thrown, I put the necessary code to generate a screen capture in the onException method. You can see from the list above that you can have Selenium perform an action when other events occur. For example, if you wanted to do a screen capture after each click() action, you could write:

public void afterClickOn(WebElement element, WebDriver driver) {
    String filename = generateRandomFilenameFromWebElementAndDriver(element, driver);
    createScreenCaptureJPEG(filename);
}

I'll leave it to the reader to figure out how to create the generateRandomFilenameFromWebElementAndDriver. You might do something like use getCurrentUrl(), convert things like colon, slash, etc. into valid filename characters then append the web element getTagName() to the end of the filename.

Or you could do something tricky like have the beforeClickOn generate the filename and the afterClickOn use that filename to generate the screen capture.

If the event you want to trigger a screen capture is not listed above, there is no easy answer for how you would do it.

Initially, you might considering changing the WebDriverEventListener interface. But this would require a change to how Selenium works. Every time there is a new release of Selenium, you would have to merge your changes back in.

I would recommend submitting the changes as a new feature to the Selenium project to see if they'll incorporate it or wrap the Selenium methods with your own methods. For example, you would call your sendKeys() which could do a driver.sendKeys then do a screen capture.

Wednesday, April 4, 2012

Regular Expression


I've been processing files and data a lot lately which means I've been using Regular Expressions.

Regular Expressions is a very powerful pattern matching tool. If you have used MSDOS or Bourne shell you are familiar with wildcards like "*.txt" will match all files ending with .txt. Regular expressions take this to a whole new level.

First thing to note is there are different implementations of Regular Expression. The basic concepts are the same and most the syntax is the same but there are subtle differences. I'll talk more about this as I give examples of the language.

The second thing to note is, some of the special symbols from MSDOS or Bourne shell are used by Regular Expression but they have a completely different meaning. Most notably is the asterisk (*).

The example above, "*.txt", would be a bad Regular Expression. Why? The asterisk means the previous character zero or more times. There is no character preceding the asterisk so it is a syntax error.

For simple things like "*.txt", Regular Expression can be overly complex. The dot (.) means any character. So if I want to emulate the "*" of MSDOS, I would use ".*" in Regular Expression. If I wanted an actual dot I would use "\." in Regular Expression. So the whole "*.txt" in MSDOS becomes ".*\.txt" in Regular Expression. In most languages, the "\." would get processes as a control character by the String implementation. The slash (\) would never make it to the Regular Expression parser. So if you want "\." to reach the Regular Expression parser, you need to use "\\." because the String implementation will parse this, resulting in "\.", then pass it to the Regular Expression parser.

The language I use most right now is Java. If you look at the Java API documentation for the Pattern class you will see this is the Regular Expression parser.

Some of the basic stuff:

  • Anything not a special character is matched verbatim. So in my example above "txt" only matches "txt".
  • If you want to match a special character you need to escape it. From my example, the dot is a special character. To match a dot and nothing else you use "\\.". I use double slash because Java will parse the "\\" before sending it to Pattern.
  • Special characters from things like println() or printf() work the same in Regular Expression. These are "\t" for tab, "\n" for newline, "\r" for return, "\f" for form-feed, "\a" for a bell. A bell in ASCII is control-g or "\x07" but "\a" is better because you shouldn't assume ASCII.
  • You can have a sets using square brackets. If I have "[abc]" this will match "a", "b" or "c".
  • You can use the square brackets for negation. If the first character in the set is caret (^) it means 'not'. For example, "[^abc]" would match anything not "a", not "b" and not "c".
  • If you want all digits you could use "[0123456789]" but there is a shorthand for this. A range can be specified using a minus (-) symbol. This example would be "[0-9]". You can also do things like "[a-z]" but alphabetic strings can be problematic if you allow different character sets.
  • If you want upper and lower case letters you might think "[a-Z]" would work but this is an error. The letter 'Z' in ASCII has a value of 90 and 'a' has a value of 97. Second attempt might be "[A-z]". This is closer but in ASCII the symbols '[', '\', ']', '^', '_' and '`' are between 'Z' and 'a'. So you have too many characters in this set. The solution is a union (like in Set Theory). You want "[a-z]" union "[A-Z]". In Regular Expression this is written as "[a-zA-Z]".
  • You can also write a union as "[a-z[A-Z]]". This might seem like extra typing and in some cases it is. What if you wanted all consonants? That would be 21 letters uppercase and 21 letters lower case. A string with 42 letters (you cannot really use a single range). You could use "[b-df-hj-np-tv-zB-DF-HJ-NP-TV-Z]" but even that is a little ugly. How about: "[a-zA-Z[^aeiouAEIOU]]". When I look at that it is pretty obvious what I'm trying to match. It reads as "all letters but not vowels".
  • There is 'syntactic sugar' for some things:
    • Rather than "[0-9]" I can use "\d" (the d is for digit)
    • Rather than "[^0-9]" I can use "\D" (uppercase implies NOT)
    • Rather than "[ \t\n\x0b\\f\r]" I can use "\s" (the s is for space or whiteSpace)
    • Rather than "[^ \t\n\x0b\\f\r]" I can use "\S" (uppercase implies NOT)
  • A 'word' is a String made of letters, digits or underscore. A character of a 'word' therefore would be: "[a-zA-Z\d_]". Syntactic sugar for this is "\w".
  • Alternately, "\W" is for not a 'word' character.

Some slightly more advanced stuff would be boundary qualifiers:

  • The caret (^) not in a set means beginning of line. So if I have the string "^a" it matches if 'a' is the first character in the string. With wildcards or substring matching this can be very helpful. For example, "^def" will not match a substring check with "abcdefghi" but "def" will match.
  • The dollar ($) is for end of line. For example, "def$" will not match "abcdefghi" but "def" will match.
  • Capture groups are used for substitution. For example, if I have a string with my full name, "Darrell Grainger" and I want to change it to "Grainger, Darrell" I would do the following:
String name = "Darrell Grainger";
String flip = name.replaceFirst("(\\w*) (\\w*)", "$2, $1");
  • The "\\w*" means get the first word. It will match "Darrell". By wrapping it with parenthesis it becomes a 'capture group'. So the first "(\\w*)" gets saved into "$1" and the second "(\\w*)" gets saved into "$2".  In other implementations of Regular Expression, capture groups are saved into things like "\1" rather than "$1".
  • Capture groups are great if you are processing a number of strings in an array. This example will flip the first and second word for any set of strings.
More advance stuff would be Greedy quantifiers versus Reluctant quantifiers. Lets look at this with capture groups.
String s = "aaabbbaaa";
String s1 = s.replaceFirst("(a*)(.*)", "$2 $1");
String s2 = s.replaceFirst("(a*?)(.*)", "$2 $1");
The string s1 will contain "bbbaaa aaa".
The string s2 will contain "aaabbbaaa ".

For s1, what happened is "(a*)" matched "aaa" and "(.*)" matched "bbbaaa".
For s2, what happened is "(a*?)" was a Reluctant quantifier. Because "(.*)" is a Greedy quantifier, it captured everything. This left nothing for "(a*?)" to capture.

What happens under the hood is that the Regular Expression parser will find the Greedy quantifiers, read in the entire string and see if it matches. If it does not it pushes one character back out, checks for a match, pushes a character back out, checks for a match. It keeps doing this until it finds a match. Whatever didn't match is used to process Reluctant quantifiers. 

While processing the Reluctant quantifiers the parser will read in one character, check for a match, read another character, check for a match, read another character, check for a match. It keeps doing this so long as things are matching. The moment there isn't a match it stops.

So the s1 string processed "(a*)" first, because it is a Greedy quantifier and captured "aaa" into "$1". Then it processed "(.*)" which matched the rest of the string. This captured "bbbaaa" into "$2".

With the string s2 it processed "(.*)" because it is a Greedy quantifier and "(a*?)" is a Reluctant quantifier. The "(.*)" grabbed the entire string and put it into "$2". This left an empty string "". The empty string is used to process the Reluctant quantifier "(a*?)" and "" gets captured into "$1".

Here is a table of the Greedy versus Reluctant quantifiers:


Greedy Reluctant Meaning
X? X?? X, once or not at all
X* X*? X, zero or more times
X+ X+? X, one or more times
X{n} X{n}? X, exactly n times
X{n,} X{n,}? X, at least n times
X{n,m} X{n,m}? X, at least n but not more than m times


There is more the Regular Expressions but this information is what you need for most situations.
.

Tuesday, February 14, 2012

My environment for Selenium 2.0 (WebDriver)

One thing I believe to be very important for any sort of software development (and test automation with Selenium is software development) is having a good environment. So here is how I set up my environment to start with Selenium 2.0 and Java.

What you need:

  1. Java (go to  http://www.oracle.com/technetwork/java/index.html)
    1. I typically go with Java SE 1.6.0. If you don't need the latest, go with something more mature.
    2. Download the full SDK and docs.
    3. Install them in C:\jdk1.6.0_30 (assuming you downloaded build 30)
    4. I like to put it in the root of the C drive with no spaces in case other things don't like spaces.
    5. Unpack the docs into the same directory so they are easy to find. 
  2. Eclipse (go to  http://www.eclipse.org/downloads/)
    1. I typically go with Eclipse IDE for Java Developers because it has everything for Selenium and it is small.
  3. SVN (go to  http://subversion.apache.org/)
    1. You need some sort of source control.
    2. I usually go with Git but more people are still using Subversion.
    3. There are more tutorials on SVN and better support.
  4. Selenium (go to  http://code.google.com/p/selenium/downloads/list)
    1. You'll need to download:
      1. selenium-java-2.19.0.zip (or whatever the latest version is)
      2. selenium-server-standalone-2.19.0.jar (or whatever the latest version is)
    2. Unpack the zip file to some location
      1. I'll typically unzip them into the workspace for the project
      2. I'll also put the jar file in the same location
      3. This way I can add the jar files to subversion
  5. Eclipse support for SVN (go to  http://subclipse.tigris.org/ )
    1. Normally I would suggest using Marketplace from the Help menu but not for the latest SVN client.
    2. I like subclipse for an SVN client.
    3. Once on the subclipse site go to download and install.
    4. Copy the link for Eclipse Update Site URL for 1.8.x
    5. Go to Eclipse Help->Install New Software...
    6. Click Add...
      1. Name: subclipse 1.8.x
      2. Location: the update site URL you copied above.
    7. Select all
    8. Unselect Mylyn 3.x if you don't want it.
    9. Follow the install wizard
  6. Adding the JDK to Eclipse
    1. Go to Window->Preferences in Eclipse
    2. Expand Java
    3. Select Installed JREs
    4. Click Add...
    5. Select Standard VM
    6. Set JRE home to C:\jdk1.6.0_30\jre
    7. Set JRE name to JDK 1.6.0
    8. Everything else can be left as default.
  7. Creating a project
    1. Select File->New->Java Project
      1. Project name: something relating to the application you are testing
      2. I usually go with the defaults.
      3. Maybe change the JRE.
      4. Click Next >
      5. On the Libraries tab
      6. Add JARs... (add External JARs... if you didn't put them in the project workspace)
      7. Go to the location you unzip the Selenium files.
      8. Select the selenium-java-2.19.0.jar and selenium-java-2.19.0-src.jar files and add them.
      9. Add JARs... (add External JARs... if you didn't put them in the project workspace)
      10. Go to the location you unzip the Selenium files.
      11. Go to the libs folder and add all the jar files in there.
      12. Add JARs... (add External JARs... if you didn't put them in the project workspace)
      13. Go to the location you downloaded the selenium-server-standalone-2.19.0.jar file
      14. Add this file to the libraries.
    2. Finish the rest of the project creation using defaults.
    3. You can now start adding JUnit Test Cases to the project
      1. I select JUnit 4
      2. I select a package which follows Java conventions
      3. For example, com.mycompany.selenium.application where application is the application we are testing.
      4. Add a setUp() and tearDown()
      5. Finish
    4. Have a look at  http://seleniumhq.org/docs/03_webdriver.html for what to put in the setUp(), tearDown() and test cases.

The most important thing about using Selenium with Java is that you really need to know Java, Source Control (Subversion), an IDE (Eclipse) and a test framework (JUnit) before you even start using Selenium. So search the web for basic Java tutorials, play with Eclipse, check out http://www.junit.org/ and search for tutorials on JUnit 4 then look at the docs on Selenium HQ.

If you need help with Selenium and WebDriver, post a message to http://groups.google.com/group/webdriver. I usually respond to people there on a regular basis.

Tuesday, January 17, 2012

Getting string from the clipboard in Java

Recently needed Java code to get the contents of the clipboard. For your reading pleasure:


public static String getStringFromClipboard() {
String s = null;
Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable contents = c.getContents(null);
if(contents != null && contents.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
Object o = contents.getTransferData(DataFlavor.stringFlavor);
if (o instanceof String)
s = (String)o;
} catch(UnsupportedFlavorException ufe) {
ufe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
return s;
}


This method assumes you are getting a string from the clipboard.

Wednesday, November 30, 2011

Interface versus implementation

I've seen a few people posting Java code for Selenium automation where they have things like:

FirefoxDriver driver = new FirefoxDriver();

If you look at the code for Selenium you will see they have an interface called WebDriver. FirefoxDriver, InternetExplorerDriver, HtmlUnitDriver, RemoteWebDriver all implement WebDriver.

If I'm writing a test suite, I ultimately want to run the suite on Firefox, Internet Explorer, Chrome, etc. but if I implement my framework using FirefoxDriver, I have to edit the code to make it work on InternetExplorerDriver. The proper solution is to write your code using:

WebDriver driver = new FirefoxDriver();

You might argue that I need to change my code still. Okay, so you would implement it using:

WebDriver driver;
    if(browserType.equals("firefox")) {
        driver = new FirefoxDriver();
    } else if(browserType.equals("ie")) {
        driver = new InternetExplorerDriver();
    } else {
        driver = new RemoteWebDriver();
    }

Now all subsequent code will use driver as if it was merely a WebDriver. However, if you look at RemoteWebDriver you will see that it also implements JavascriptExecutor. So if I wanted to use:

driver.executeScript(script, args);

I'll get an error because WebDriver does not support executeScript. My driver is REALLY a RemoteWebDriver and does support executeScript. So how do I access that functionality? Quite simple:

((RemoteWebDriver)driver).executeScript(script, args);


Even better would be:

if(driver instanceof RemoteWebDriver) {
        ((RemoteWebDriver)driver).executeScript(script, args);
    } else if(driver instanceof FirefoxDriver) {
        ((FirefoxDriver)driver).executeScript(script, args);
    } else if(driver instanceof InternetExplorerDriver) {
        ((InternetExplorerDriver)driver).executeScript(script, args);
    }


Now if you run the test suite against a browser that does not support a particular feature, it will skip that part of the code automatically.

And that is proper use if polymorphism.

Friday, February 25, 2011

The Platform class in Selenium 2

There is a class in Selenium 2 called Platform. It is very helpful in those situations where you need to do something different for different platforms. Here is some sample code:

 @Test
 public void seleniumPlatformExamples() {
  System.err.println("Supported platforms:");
  for(Platform p : Platform.values()) {
   System.err.println(p.toString());
  }
  System.err.print("The current platform is " + Platform.getCurrent());
  System.err.printf(" v%d.%d\n", Platform.getCurrent().getMajorVersion(), Platform.getCurrent().getMinorVersion());
 }

The Platform.getCurrent() will return the current instance of Platform. You can then access all the other features using that instance.

There are other functions like checking to see if current version is equal to some enum value.

Thursday, February 24, 2011

Generating a screen capture on exception thrown with Selenium 2

Recently, someone asked how to have Selenium 2 (WebDriver) create a screen capture if an exception is thrown. Here is how you do it...

Let's say you have the code:

WebDriver driver;

    @Before
    public void setUp() {
        driver = new FirefoxDriver();
    }

and you want to change it so the test cases will generate a screen capture file when an exception is thrown. Here is the first change:

WebDriver driver;

    @Before
    public void setUp() {
        WebDriverEventListener eventListener = new MyEventListener();
        driver = new EventFiringWebDriver(new FirefoxDriver()).register(eventListener);
    }

The second change is to create the MyEventListener class. The MyEventListener class will be:

public class MyEventListener implements WebDriverEventListener {
    // All the methods of the WebDriverEventListener need to
    // be implemented here. You can leave most of them blank.
    // For example...
    public void afterChangeValueOf(WebElement arg0, WebDriver arg1) {
        // does nothing
    }

    // ...

    public void onException(Throwable arg0, WebDriver arg1) {
        String filename = generateRandomFilename(arg0);
        createScreenCaptureJPEG(filename);
    }
}

The MyEventListener class will have 15 methods, including the two examples I have given here. The main method that you must implement if you want screen captures whenever an exception is thrown would be the onException method.

The biggest trick for this method is generating a unique filename for each exception. First thought is that the filename could be in the format "YYYY-MM-DD-HH-MM-SS.jpg". Unless you get two exception in one minute this will work okay. Unfortunately, it will be hard to figure out what the exception was unless you kept some sort of log in the code execution. You'll also have to waste time figuring out which exception goes with which date/time.

Personally, I'd use the format "YYYY-MM-DD-HH-MM-SS-message-from-the-throwable-argument.jpg". Selenium tends to throw multiple line exception messages. So you could take the first line of the message, change characters which are illegal for your file system and change them to underscores. You could also have something to set the location of the screen capture files and prepend that to the filename.

Here is the code I came up with in 2 minutes:

private String generateRandomFilename(Throwable arg0) {
        Calendar c = Calendar.getInstance();
        String filename = arg0.getMessage();
        int i = filename.indexOf('\n');
        filename = filename.substring(0, i).replaceAll("\\s", "_").replaceAll(":", "") + ".jpg";
        filename = "" + c.get(Calendar.YEAR) + 
            "-" + c.get(Calendar.MONTH) + 
            "-" + c.get(Calendar.DAY_OF_MONTH) +
            "-" + c.get(Calendar.HOUR_OF_DAY) +
            "-" + c.get(Calendar.MINUTE) +
            "-" + c.get(Calendar.SECOND) +
            "-" + filename;
        return filename;
    }

The final part is the code to actually generate the file. This is standard Robot stuff. Here is the code I whipped together a few projects back:

private void createScreenCaptureJPEG(String filename) {
  try {
   BufferedImage img = getScreenAsBufferedImage();
   File output = new File(filename);
   ImageIO.write(img, "jpg", output);
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
 
 private BufferedImage getScreenAsBufferedImage() {
  BufferedImage img = null;
  try {
   Robot r;
   r = new Robot();
   Toolkit t = Toolkit.getDefaultToolkit();
   Rectangle rect = new Rectangle(t.getScreenSize());
   img = r.createScreenCapture(rect);
  } catch (AWTException e) {
   e.printStackTrace();
  }
  return img;
 }

And that is it. Whenever an exception is thrown a file will be generate.

Monday, February 21, 2011

Using Selenium 2.0 with WebDriver and Safari

I've been looking at Selenium 2.0 and writing test cases using WebDriver. Looking at the APIs I see there is a class for Android, iPhone, InternetExplorer, Firefox and Chrome which extends the RemoteWebDriver.

So how do I use Safari? The code to set the search string for Google, using WebDriver, would be:

WebDriver browser = new FirefoxDriver();

browser.get("http://www.google.com");
WebElement input = browser.findElement(By.name("q"));
input.sendKeys("Selenium");

If I wanted to do the same thing using Safari web browser, I could use:

Selenium browser = new DefaultSelenium("localhost", 4444, "*safari", "http://www.google.com");

browser.type(name("q"), "Selenium");

The problem with this is I need to do things differently for Safari. I have to use Selenium 1.0 commands to test Safari and Selenium 2.0 for everything else. So how can I use a browser which was supported in Selenium 1.0 with all the Selenium 2.0 APIs?

The solution is:

Selenium sel = new DefaultSelenium("localhost", 4444, "*safari", baseURL);
CommandExecutor executor = new SeleneseCommandExecutor(sel);
DesiredCapabilities dc = new DesiredCapabilities();
WebDriver browser = new RemoteWebDriver(executor, dc);

browser.get("http://www.google.com");
WebElement input = browser.findElement(By.name("q"));
input.sendKeys("Selenium");

If you look at this block of code, the last 3 lines are the same as the last three lines of the first block of code. Essentially, I add the first three lines then change the WebDriver declaration to a new RemoteWebDriver.

The one thing I found however, on my Mac OS X, if I started the SeleniumServer with setSingleWindow(false) it would fail to work. I run my SeleniumServer inside my Java code using:

private static SeleniumServer ss;
private static RemoteControlConfiguration rcc;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
rcc = new RemoteControlConfiguration();
rcc.setInteractive(true);
rcc.setSingleWindow(true);
rcc.setTimeoutInSeconds(10);
ss = new SeleniumServer(rcc);
ss.start();
}

If you are running the SeleniumServer from the command line, you'll have to look at the command line options to ensure you are running in Single Window mode.

Saturday, February 12, 2011

Selenium 2

I've been to the dark side (HP Quick Test Professional) for the past year but I'm back to working with open source tools like Selenium.

When I last looked at Selenium it was just in the process of releasing version 2.0.

One of my complaint of 1.0 was how inconsistent the Java APIs were. Simple things like iterating through all the OPTIONS on a SELECT list wasn't really there. You could use Selenium to make the application do anything a user would do but for test automation you need to have access to information that a manual test would usually validate with their eyes.

I recently started looking at version 2.0. There seems to be a great deal of improvement.


  1. It will still run the Selenium 1.0 code with no change at all.
  2. You can mix and match 1.0 and 2.0 code which enables you to slowly refactor your old 1.0 automation to 2.0 over time.
  3. No more effort trying to create a jUnit 3 case to extend all your test cases from.
  4. A much more simple design with far less 'extras' to help you, i.e. a K.I.S.S. approach to the APIs.

In the old Selenium 1.0 we would create an instance of Selenium and it would be the gateway to the browser. With Selenium 2.0 we create an instance of WebDriver. WebDriver is a base class of classes that represent the different browsers. If you wanted to create a fast, non-GUI browser you can use the HTMLUnit browser. For example, using jUnit 4 annotations, your @Before method would be:

WebBrowser browser;

@Before
public void setUp() {
    browser = new HtmlUnitDriver();
}

Now all your test cases (@Test) can you the browser instance to access the APIs. If you then want to change the test to run on a real web browser you can use:

    browser = new FirefoxDriver();
or
    browser = new InternetExplorerDriver();
or
    browser = new IPhoneDriver();
or
    browser = new AndroidDriver();

As new browsers are added to the WebDriver family this list will grow.

One you have a browser instance there is a limited number of options:

browser.findElement(by);
browser.findElements(by);
browser.get(url);
browser.getTitle();
browser.navigate();
browser.switchTo();

The biggest methods are the findElement and findElements. The first will find a single element. The input is the By class. It can identify elements by name, id, xpath, etc. If the identifier matches more than one element, it will return the first match. Realistically, I can see this changing with different browsers or at least changing over time as systems get upgraded. You really want to make sure the identifier matches a single, unique element.

The second method, findElements, works like the first method but it returns a list. Most elements are WebElements (some exceptions). So the first method returns a single WebElement and the second method returns a List<WebElement>. The best thing is you can find ANY html element. 

Once you have a WebElement, you have the following list of methods:

element.clear();
element.click();
element.findElement(by);
element.findElements(by);
element.getAttribute(attributeName);
element.getTagName();
element.getText();
element.getValue();
element.isEnabled();
element.isSelected();
element.sendKeys(keysToSend);
element.setSelected();
element.submit();
element.toggle();

The findElement and findElements will search from the given element and down in the DOM. For example, if you use browser.findElement to find a SELECT tag then you can use the result of that to find all OPTION tags. It would then be searching under the SELECT tag and therefore find all the OPTIONs for that tag.

That is essentially it. K.I.S.S. Rather than using arrays it is using Lists and Iterators. If the elements are uniquely identified and don't move around much it could be easy to maintain elements. In many cases the developer will find he needs to change a DIV to a SPAN or an A to a IMG with an onclick attribute. Rather than searching for a DIV you want to encourage the use of id attributes (which must be unique according to HTML standards) you can then find everything By id. A change in the tag or location in the DOM would require no maintenance of the automation scripts.

More to follow...

Tuesday, March 16, 2010

Selenium RC [Java] waitForCondition example

I've seen a few people post questions about using the Selenium waitForCondition method.

If you read the documentation for the waitForCondition method (in either Selenium or DefaultSelenium) you will see a mention of selenium.browserbot.getCurrentWindow(). This is key to the wait for condition.

When you start a test case there are two windows which open up. The first window has the Selenium Command History and ability to display the log. I call this the Selenium window. The next window which opens is the window for the application under test or AUT window.

The waitForCondition method has two parameters. The second parameter is the easiest to understand. We don't want the test to wait for ever. If we don't get a response by a certain time we can assume the test failed. The second parameter is the timeout period in milliseconds. So if the condition should occur within 20 seconds, the second parameter is "20000".

The second parameter is the key to the waiting. It is the condition we are waiting for. There are two windows open, Selenium and AUT. The first parameter to waitForCondition is a javascript expression. It will normally be tested on the Selenium window. If you want to evaluate some javascript in the AUT window you need to use: selenium.browserbot.getCurrentWindow(). For example, Let's say this is the AUT window:



<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
// this variable is the key. When it is false the loading is done
var loading=true;

function loadingDone() {






// hidden the GIF
document.getElementById("loading").style.visibility="hidden"; // set the flag to indicate we are done loading
loading=false;
}
// this sets a timer for ten seconds to simulate something
// loading for ten seconds. After ten seconds this will call
// the loadingDone() function.
var seconds=10;
var t=setTimeout("loadingDone()", seconds * 1000);
loading=true;

</script>
<!-- the moment the page loads this will create -->
<!-- a spinning GIF to signal something loading -->
<img id="loading" src="http://www.oshawa.ca/images/loading.gif" style="visibility:visible"/>
</body>
</html>


What this page does is simulate something loading. After 10 seconds it will hide the spinning GIF and set the loading variable to false. So the condition we are waiting for is when loading == false.

This means from waitForCondition we want to look at the variable "selenium.browserbot.getCurrentWindow().loading == false". In other words, the full statement is going to be:

selenium.waitForCondition("selenium.browserbot.getCurrentWindow().loading == false", "20000");

It is important to note that the selenium in selenium.waitForCondition is a Java object in your test code and the selenium in selenium.browserbot is a javascript object running in the web browser.

Let's say we take this HTML and save it to the file /Users/darrell/workspace/LearningSelenium/waitForCondition.html then we can create the following Selenium RC test case:



package com.example.tests;

import org.openqa.selenium.server.RemoteControlConfiguration;
import org.openqa.selenium.server.SeleniumServer;
import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.SeleneseTestCase;

public class TestingSelenium extends SeleneseTestCase {

 SeleniumServer ss;
 RemoteControlConfiguration rcc = new RemoteControlConfiguration();

 public void setUp() throws Exception {
  int ssPort = 4444;
  rcc.setPort(ssPort);
  rcc.setTimeoutInSeconds(1200);
  ss = new SeleniumServer(rcc);
  ss.start();
  selenium = new DefaultSelenium("localhost", ssPort, "*safari", "file://");
  selenium.start();
 }

 public void testStub() throws Exception {
  long start, stop;
  selenium.open("/Users/darrell/workspace/LearningSelenium/waitForCondition.html");
  start = System.currentTimeMillis();
  selenium.waitForCondition("selenium.browserbot.getCurrentWindow().loading == false", "300000");
  stop = System.currentTimeMillis();
  System.err.println("Elapsed time: " + ((stop - start) / 1000.0) + " seconds");
 }

 public void tearDown() throws Exception {
  super.tearDown();
  ss.stop();
 }
}


and that is one example of waitForCondition. You will have to know what condition to wait for in your application. If you are using AJAX you might find there is an AJAX object on your application. In it will be a activeRequestCount. When the AJAX.activeRequestCount goes to zero, all the AJAX calls are done. So you can wait for an AJAX call to complete with:


selenium.waitForCondition("selenium.browserbot.getCurrentWindow().AJAX.activeRequestCount == 0", "30000");

Monday, March 1, 2010

How to reduce code duplication, part 2

In a previous entry I blogged about how to reduce code duplication. My solution is using libraries.

Let's say I have a web application for writing blogs. Okay, I'm actually going to use the Blogger software as my example. First thing I'm going to do is break the application down into manageable parts. Across the top is:
  • Posting
  • Settings
  • Layout
  • Monetize
  • View Blog
Each one will take me to a new page. I typically automate with Selenium using Java. So I might create one class for each of these areas. However, when I select Posting I see it has the following subsections:
  • New Post
  • Edit Posts
  • Edit Pages
  • Comment Moderation
So I might want to create the following packages:
  • com.google.blogger.posting
  • com.google.blogger.settings
  • com.google.blogger.layout
  • com.google.blogger.monetize
  • com.google.blogger.viewblog
Then within the com.google.blogger.posting package I'm going to create the following classes:
  • NewPost
  • EditPosts
  • EditPages
  • CommentModeration
Next I'm going to focus on one class at a time. Additionally, I'm going to create Java classes to represent the data on the application. For example, in the Posting section, on the New Post page I have the following:
  • Title
  • Link
  • HTML (the HTML text in a text editor)
  • Labels
  • Post Options
    • Reader comments
    • Backlinks
    • Post date and time
    • Edit HTML Line Breaks
    • Compose Settings
All this is data for the page so I'd create:

class NewPostForm {
        String title;
        String link;
        String htmlBody;
        String labels;
        PostOptions postOptions;
    }

    class PostOptions {
        boolean allowReaderComments;
        boolean allowBacklinks;
        boolean enterIsLineBreak;
        boolean htmlShownLiterally;
        boolean automaticPostDateTime;
        String scheduledAtPostDate;
        String scheduledAtPostTime;
    }

I would also add getters, setters and constructors to these classes, e.g.

public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

Now, if I create a library function to fill in a new post, I don't have to pass in a dozen or more parameters. I can create an instance of NewPostForm, populated it then pass it in to my library function. So back to the NewPost class. This is the library of routines to be used by my test suite:

class NewPost {
        Selenium selenium;

        public NewPost(Selenium selenium) {
            this.selenium = selenium;
        }

        public void gotoNewPost() {
            // if a link to New Post exists then
                // click the link
                // wait for the new post page to appear
                // this might be a page load so WaitForPageToLoad
                // or it might be AJAX so waitForCondition or
                // whatever the case may be
           // else
                // fail the test case
        }

        public void fillInNewPostForm(NewPostForm npf) {
            assertNotNull(npf);
            assertNewPostForm();
            if(npf.getTitle() != null) {
                // set the title input field to npf.getTitle()
            }
            // more of the same thing for each field in npf
        }

        public void assertNewPostForm() {
            assertTrue(selenium.isElementPresent(titleTextFieldLocator));
            assertTrue(selenium.isElementPresent(linkTextFieldLocator));
            // more of the same for all the inputs in the form
        }

        public void clickPublishPost() {
            // if a link for Publish post exists then
                // click the link
                // wait for the post to get published
            // else
                // fail the test case
        }
    }

I've left out things like the values for titleTextFieldLocator but that is easy enough. It would be something like:
String titleTextFieldLocator = "xpath=//input[@id='postingTitleField']";
As you build things up you will have a library of methods for the different 'pages' of the application. So if I wanted to test all the different ways of posting something I could have an instance of NewPost as a class variable for a test case then initialize the variable in the @Before method, just after the initialization of the selenium instance. For example:

class NewPostTestCases extends SeleneseTestCase {
    NewPost np;

    @Before
    public void setUp() throws Exception {
        super.setUp(); // initializes inherited selenium
        np = new NewPost(selenium);
    }

    @TestCase
    public void someTest() throws Exception {
        np.gotoNewPost();
        NewPostForm npf = new NewPostForm();
        npf.setTitle("How to reduce code duplication, part 2");
        npf.setHtmlBody("this is my new blog body");
        npf.setLabels("selenium, java, testing, development");
        np.fillInNewPostForm(npf);
        np.clickPublishPost();
        // do some sort of assertion to confirm the post went okay
    }

    @After
    public void tearDown() throws Exception {
        super.tearDown();
    }
}

And that is the basic idea. I'd used my IDE to generate all the getters/setters and constructors. I'd also let it suggest when I needed to throw an exception (I'd always throw the exception rather than try/catch and deal with it; junit was made assuming the exception gets thrown up to the TestRunner). And there are things like, put the datatypes in a different package, e.g. com.google.blogger.posting.datatypes for the NewPostForm datatype plus let the IDE tell you when to add the appropriate import statements.

Wednesday, February 17, 2010

multiple versions of Java for Eclipse

If you are using Eclipse you can use different version of Java to run your applications. When you create a new Java project, one of the settings is to select a JRE. There is also a link to configure the JREs.

First thing you will need is multiple JREs. If you go to http://java.sun.com/products/archive/ you should be able to find different versions of Java for your computer. You are mostly interested in the Java Platform Standard Edition (Java SE) and Java 2 Platform Enterprise Edition (J2EE). Download and install a few. I usually install them in C:\ on Windows so they are easy to find and unaffected by Windows Update.

In Eclipse, if you go to the link to configure JREs you will come to a dialog with a list of JREs (probably only one maybe two entries) and an Add... button. Click the Add... button, select Standard VM, then browse to the home of the JRE you installed. If you installed the JRE into C:\jre142_5 then this is the JRE home you are looking for. If you installed a JDK it is probably something like C:\jdk142_5.

Once you have added all the different JREs and JDKs you have installed, you can use them in new and existing projects.

For a new project, during the setup wizard you can select a project specific JRE from the list of JREs you have identified for the workspace.

For an existing project, right click on the Project name in the Package Explorer. From the menu select Build Path->Configure Build Path... option. In the dialog which appears you should see a list of Libraries for the project. One of the entries will be the JRE or JDK. Select it then click the Edit... button. On the dialog which appears you can select one of the JRE/JDKs you just added to the system.

Monday, February 15, 2010

Creating a Selenium Test Case in Java from scratch

I you want to create a Selenium test case from scratch, i.e. not using the record method listed earlier in my blog, here is how you do it:


  1. I assume you have Eclipse installed
  2. I assume you have downloaded the Selenium RC
    1. The files selenium-server.jar and selenium-java-client-driver.jar will be needed
  3. In Eclipse create a new Java Project
    1. Pick a name for the project, set anything else you think you'll need on the first page
    2. On the next page, go to the Libraries tab and add the two selenium jar files to the project
    3. Finish
  4. Right click on the src folder and create a new JUnit Test Case
    1. Select JUnit 3, pick a package if you want (can be changed later), pick a name, tick setUp and tearDown, click Finish
    2. It should ask you if you want to add the JUnit 3 library to the project. Answer yes.
  5. You should now be looking at the JUnit test case class in the editor
  6. Change the class so it 'extends SeleneseTestCase
  7. You should get a warning on the SeleneseTestCase
  8. Hover over it and you should get the option to import com.thoughtworks.selenium.SeleneseTestCase which you should do
  9. In the setUp() method, the body should be:
    1. super.setUp(url, browser); where url is the URL of you web site being tested and browser is something like "*firefox", "*iehta" or "*safari"
    2. For example, super.setUp("http://www.google.ca", "*safari");
    3. The setUp method should now look like:



      public void setUp() throws Exception {
              super.setUp("http://www.google.ca", "*safari");
          }
      
      
  10. In the tearDown() method the body should be:
    1. super.tearDown();
    2. The tearDown method should now look like:



      public void tearDown() throws Exception {
              super.tearDown();
          }
      
      
  11. Now we can add a test case. It might look like:
  12. public void testAGoodDescriptionOfWhatWeAreTesting() throws Exception {
            selenium.open("/");
            System.out.println(selenium.getHtmlSource());
        }
    
    
  13. To run this, you'll need to start the Selenium Server.
  14. Go to a command line and enter:
  15. java -jar selenium-server.jar
    
  16. You might need some more command line switches like -firefoxProfileTemplate or -trustAllSSLCertificates. To see help on the server use:
  17. java -jar selenium-server.jar -help
    
  18. Once you have the server running, in Eclipse you want to run the test case as a JUnit Test
NOTE: the SeleneseTestCase is a JUnit 3 TestCase. It assumes the names of the methods are fixed and does not use annotations. You have to use setUp, tearDown and all test cases need to start with 'test'. If you want to create the same thing as JUnit 4 you can use:

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.Selenium;

public class Whatever {

 Selenium selenium;

 @Before
 public void setUp() throws Exception {
  selenium = new DefaultSelenium("localhost", 4444, "*firefox", "http://www.google.com");
 }

 @Test
 public void whatever() throws Exception {
  selenium.open("/");
  System.out.println(selenium.getHtmlSource());
 }

 @After
 public void tearDown() throws Exception {
  selenium.close();
  selenium.stop();
 }
}

And there is a simple test case create in Eclipse.