Monday, September 30, 2013

Examine ExtJS Store Data (Selenium Web Driver + JUnit + ExtJS)

Part of the goal of unit test is to check result against expected values using various assertions. An essential data structure in ExtJS web application is store. So, no test case is complete without some assertion against the stores. For example, we might want to verify number of records, or value of certain column, ... Here are some code samples to access store data, get number of rows, or retrieve value of a column:

//this example assuming storeID is known
ArrayList<Object> storeData = (ArrayList<Object>) ((JavascriptExecutor) driver)
.executeScript("return _.pluck(Ext.StoreManager.get(storeID).getRange(), 'data');", storeID);
//get number of rows in the store's data
int numberOfRows = storeData.size();
//get the first row, you are going to see a JSON string on Java console
System.out.println(storeData.get(0));
//get the first row's value for the column "name"
String name = ((Map<String, Object>) storeData.get(0)).get("name").toString();

Saturday, September 28, 2013

Wait for AJAX Complete (Selenium Web Driver + JUnit + ExtJS)

With AJAX, Seleinum testing can have a lot of tricky timing issue. Tests can fail due to slightly delayed AJAX response. Implicit wait solved the problem to some extent, but would be nicer to have a way to wait for all AJAX calls to be finished.

Here is a solution for ExtJS AJAX requests:
public waitForAjax(WebDriver driver, int timeOutInSeconds) {
(new WebDriverWait(driver, timeOutInSeconds))
.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver d) {
return (Boolean) ((JavascriptExecutor) d)
.executeScript("return !(ExtJSUtil.hasActiveAjaxCalls(Ext.Ajax.requests && !_.isEmpty(Ext.Ajax.requests));");
}
});
}
Core concept is this JS code: Ext.Ajax.requests && !_.isEmpty(Ext.Ajax.requests). It works for ExtJS 4 only, might change for future or older version of ExtJS due to how AJAX calls are managed.

Thursday, September 26, 2013

Locate ExtJS Component (Selenium Web Driver + JUnit + ExtJS)

Selenium WebDriver offers many ways to find an element. You can find it by class, name, partial link text, xpath, css selector, ... However, when it comes to ExtJS component, none of them works very well, because ExtJS page elements tend to be generated dynamically, their xpath/id/... are all moving target. Trying to figure out a repeatable way to locate any of them can be very time consuming and the result is fragile.

Fortunately, as all ExtJS developers should know, Ext.ComponentQuery provides a powerful and very reliable way to find your component. So, the idea is to use Ext ComponentQuery to find the component of interest, then pass it along to WebDriver. Along this line, below is my approach to locate components:
  • PageObject owns all the top level components, and specify a ComponentQuery that can uniquely identify them;
  • All views or components of interest have their hierarchy represented in the test code, and at each level, they should have a ComponentQuery that can unique identify each of them within the container;
  • When it's time to get the element, we should traverse up from element to page, rebuild the fully qualified component query

Ok, some sample code might explain the idea better:
//find the fully qualified component query of ExtJS component cmp
String query = cmp.componentQuery;
Component parent = cmp.parent;
while (parent != null) {
query = parent.componentQuery + " " + query;
parent = parent.parent;
}
//use component query to find id
String js = "return Ext.ComponentQuery.query(\"" + query + "\")[0].id;";
String id = (String) ((JavascriptExecutor) _driver).executeScript(js);
WebElement element = driver.findElement(By.id(id));

Wednesday, September 25, 2013

Take Screenshot on Failure (Selenium Web Driver + JUnit + ExtJS)

First a little background: this is the first of a series of lessons I learned using Selenium Web Driver to test ExtJS web application.

It's usually desirable to always take a screenshot when test failed. The concept of JUnit rules makes this an easy task. First, implement a rule as shown below:

public class TakeScreenshotOnFailRule extends TestWatcher {
protected String _folder;
protected WebDriver _driver;
public TestRule(WebDriver driver, String folder) {
_folder = folder;
_driver = driver;
}
@Override
protected void failed(Throwable e, Description description) {
super.failed(e, description);
String name = description.getDisplayName();
name = _folder + "\\" + name + ".png";
File screenshot = ((TakesScreenshot)_driver).getScreenshotAs(OutputType.FILE);
try {
FileUtils.copyFile(screenshot, new File(name));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
Then in your test classes, you just need to declare this rule, then all test methods in it will automatically take screenshot on failure.
@Rule
public TakeScreenshotOnFailRule rule = new TakeScreenshotOnFailRule(driver, folderName);
view raw gistfile1.java hosted with ❤ by GitHub
The examples here are just for illustration purpose. In real world application, you might make some improvement like:

  • Make sure the file name is legal for the OS (for example, the following characters are not allowed in Windows file name: \/:?*<>|\
  • Pictures might be further organized into folders by test cases?
  • The rule might be declared in a base class so that all test cases have the same behavior without extra boiler plate code