Test downloads with Neustar

Recently I was asked to write a Neustar script to download files from the browser. The goal of the script would be to calculate the sites performance during [X] downloads.

The following is the download step:

test.beginStep("Download");
var wrapper = driver.findElement(By.className("download-wrapper"));
// Find the name, size, and url of the download
var downloadName = wrapper.findElement(By.cssSelector(".download-info")).getText();
var downloadSize = wrapper.findElement(By.cssSelector(".size")).getText();
var downloadUrl = wrapper.findElement(By.partialLinkText("DOWNLOAD")).getAttribute("href");

// Fix name and size 
// Name: [EpicDownload.zip\n] (remove ' \n')
// Size: [99999999 bytes] (remove ' bytes') 
downloadName = downloadName.substr(0, downloadName.indexOf("\n"));
downloadSize = downloadSize.substr(0, downloadSize.indexOf(" "));

// Create the httpClient with the cookies from Selenium
var client = driver.getHttpClient();
client.setFollowRedirects(true);
client.setCheckValidSSL(false); // Test sites usually have invalid certs
client.setSocketOperationTimeout(60000); // This value will change depending on the site requirements

// If your site requires cookie authentication (Jsessionid) then we need to add the cookies
// to the httpRequest from Selenium.
var javaCookies = driver.manage().getCookies();

// Create the http GET request on the download url
var httpGet = client.newGet(downloadUrl);

// Call to custom setCookies function to add all the cookies to the http request
var httpRequest = setCookies(javaCookies, httpGet);

//Download the file to memory (do not write to the file system)
test.log("Starting download: " + downloadName + " [" + downloadSize + " bytes]");

// This will download the file from the server into memory
var httpResponse = httpRequest.execute();

// For our test we didn't need to assert anything but you could check that the download size is
// correct. Or the file name matches your expected.
var transactionStepObject = httpResponse.getInfo();
test.log("Download completed [" + transactionStepObject.getBytes() + " bytes] in " +
    transactionStepObject.getTimeActive() + "ms");
test.endStep();

//--- Util Functions

// Takes an array of javaCookies (from Selenium) and adds them to the httpRequest
var setCookies = function setCookies(javaCookies, httpRequest) {
    var javaCookiesArray = javaCookies.toArray();
    var cookieString = "";

    // Populate the cookieString with all cookies currently contained in the driver
    for (var i = 0; i < javaCookiesArray.length; i++) {
        var cookie = javaCookiesArray[i];
        cookieString += cookie.getName() + "=" + cookie.getValue() + ", domain=" + cookie.getDomain() + ", path=" +
            cookie.getPath() + "; ";
    }

    // Add the cookies as a request header
    httpRequest.addRequestHeader("Cookie", cookieString);

    return httpRequest;
};

Access the By class created using @FindBy annotation in Selenium

Recently I needed to increase the information the NoSuchElementException contained when using Selenium’s @FindBy annotation. During this I stumbled across a way to get access to the By class created from the @FindBy.

For example say we have the following PageObject


public class testPageObject {
   @FindBy(css = "h1[name='test']")
   WebElement testElement1;

   @FindBy(xpath = "//*[class='test']")
   WebElement testElement2;

   @FindBy(id = "test")
   WebElement testElement3;


   public testPageObject() {
       //Cache the WebElements using a 5 second wait
       PageFactory.initElements(new AjaxElementLocatorFactory(driver, 5), this);
   }

   public void printBy() {
        By testElement1 = getBy("testElement1");
        By testElement2 = getBy("testElement2");
        By testElement3 = getBy("testElement3");

        System.out.println(testElement1);
        System.out.println(testElement2);
        System.out.println(testElement3);
   }

   private By getBy(String fieldName) {
       try {
            return new Annotations(this.getClass().getDeclaredField(fieldName)).buildBy();
       } catch (NoSuchFieldException e) { return null; }
   }
}

The solution is to use Selenium’s built in Annotations class which takes a field and call buildBy() to return the By class for that field.

The output of the printBy() function:

By.selector: h1[name=’test’]
By.xpath: //*[class=’test’]
By.id: test

TestNG Lazy DataProvider with Factory

Last week I ran into an issue passing a lazy dataprovider to a factory. The following is the issue I first ran into:

The issue I was having is that when I ran my class it ran X (# of .xml files) number of tests using the last value contained in the array.

So if I had [“Oregon.xml”, “NewYork.xml”, “Washingtion.xml”] Washington will run 3 times.
class supplyData{

    @DataProvider
    public static Iterator<Object[]> loadStatePages(){

        List<Object[]> allParsedXMLPages = new ArrayList<Object[]>()
        int loadNextState = 0

        //create array containing x "StatePage.xml"; where x = # of states
        def allStatePages = getCityStatePages("State")

        allStatePages.each {singleStatePage ->
            def loadedStatePage = loadFile(singleStatePage)

            //create array containing [0]STATENAME, [2]ABBREVNAME, [3]ARRAY CITYNAMES
            def allStates = getStateContainer()

            //load next state to load into currentState
            def currentState = allStates[loadNextState]

            def fullStateName = currentState[0].toString()
            def abbrevState = currentState[1].toString()

            //create allCities by remove the fullStateName and abbrevState from array
            def allCities = currentState
            allCities.remove(0)
            allCities.remove(0)

            def cityLink = ""

            for (city in allCities) {
                def cityHref = city.text().replaceAll(/\W+/, '-')
                cityLink = cityLink +('<link>\n' +
                        '<title>'+ city + '</title>\n' +
                        '<linktext>' + city + '</linktext>\n' +
                        '<asserthref>/' + cityHref.toLowerCase() + '</asserthref>\n' +
                        '<asserttitle>' + city + ", " + fullStateName + '</asserttitle>\n' +
                        '</link>\n'
                )
            }

            String bodyStatePage = loadedStatePage.getText()

            ++loadNextState

            def parsedStatePage = parseString(bodyStatePage)

            def completeStatePage = createFullXMLPage(bodyStatePage, parsedStatePage)

            //add the parsed page to our ArrayList
            allParsedXMLPages.add(completeStatePage as Object[])
        }

        return allParsedXMLPages.iterator()
    }
}
public class CityTests{
    static cityCurrentXML

    public CityTests(newXML){
        this.cityCurrentXML = newXML
    }

    @Factory (dataProviderClass=supplyData.class, dataProvider="loadCityPages")
    public static Object[] getNextXML(cityCurrentXML){
        return [new CityTests(cityCurrentXML)]
    }

    @BeforeClass
    void openBrowser(){
        startBrowser()
    }

    @Test (description="Check MetaTags for correct content")
    public void MetaTags(){
        def errors = testMetaTags(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Links for existance, clickability, and target")
    public void Links(){
        def errors = testLinks(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Text for existance and accuracy")
    public void Text(){
        def errors = testText(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Overlays for existance, links, and text")
    public void Overlays(){
        def errors = testOverlays(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Hovers for existance, links, and text")
    public void Hovers(){
        def errors = testHovers(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }
    @Test (description="Check Inputs for existance, typeability, and target")
    public void Inputs(){
        def errors = testInputs(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Sliders for existance, links, and text")
    public void Sliders(){
        def errors = testSliders(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Images for existance and display")
    public void Images(){
        def errors = testImgLoaded(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check AtlasTags for existance and value")
    public void AtlasTags(){
        def errors = testAtlas(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @AfterClass
    void tearDown(){
        driver.quit()
    }
}
To fix this issue I had to make the following changes:
class supplyData{

class supplyData{

    @DataProvider
    public static Object[][] loadStatePages(){

        List<Object[]> allParsedXMLPages = new ArrayList<Object[]>()
        int loadNextState = 0

        //create array containing x "StatePage.xml"; where x = # of states
        def allStatePages = getCityStatePages("State")

        allStatePages.each {singleStatePage ->
            def loadedStatePage = loadFile(singleStatePage)

            //create array containing [0]STATENAME, [2]ABBREVNAME, [3]ARRAY CITYNAMES
            def allStates = getStateContainer()

            //load next state to load into currentState
            def currentState = allStates[loadNextState]

            def fullStateName = currentState[0].toString()
            def abbrevState = currentState[1].toString()

            //create allCities by remove the fullStateName and abbrevState from array
            def allCities = currentState
            allCities.remove(0)
            allCities.remove(0)

            def cityLink = ""

            for (city in allCities) {
                def cityHref = city.text().replaceAll(/\W+/, '-')
                cityLink = cityLink +('<link>\n' +
                        '<title>'+ city + '</title>\n' +
                        '<linktext>' + city + '</linktext>\n' +
                        '<asserthref>/' + cityHref.toLowerCase() + '</asserthref>\n' +
                        '<asserttitle>' + city + ", " + fullStateName + '</asserttitle>\n' +
                        '</link>\n'
                )
            }

            String bodyStatePage = loadedStatePage.getText()

            ++loadNextState

            def parsedStatePage = parseString(bodyStatePage)

            def completeStatePage = createFullXMLPage(bodyStatePage, parsedStatePage)

            //add the parsed page to our ArrayList
            allParsedXMLPages.add(completeStatePage as Object)
        }

        return allParsedXMLPages
    }
}
public class CityTests{
    private cityCurrentXML

    public CityTests(newXML){
        this.cityCurrentXML = newXML
    }

    @Factory (dataProviderClass=supplyData.class, dataProvider="loadCityPages")
    public static Object[] getNextXML(cityCurrentXML){
        return [new CityTests(cityCurrentXML)]
    }

    @BeforeClass
    void openBrowser(){
        startBrowser()
    }

    @Test (description="Check MetaTags for correct content")
    public void MetaTags(){
        def errors = testMetaTags(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Links for existance, clickability, and target")
    public void Links(){
        def errors = testLinks(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Text for existance and accuracy")
    public void Text(){
        def errors = testText(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Overlays for existance, links, and text")
    public void Overlays(){
        def errors = testOverlays(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Hovers for existance, links, and text")
    public void Hovers(){
        def errors = testHovers(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }
    @Test (description="Check Inputs for existance, typeability, and target")
    public void Inputs(){
        def errors = testInputs(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Sliders for existance, links, and text")
    public void Sliders(){
        def errors = testSliders(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check Images for existance and display")
    public void Images(){
        def errors = testImgLoaded(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @Test (description="Check AtlasTags for existance and value")
    public void AtlasTags(){
        def errors = testAtlas(cityCurrentXML)

        assertPageTest(errors, cityCurrentXML.PAGENAME.text())
    }

    @AfterClass
    void tearDown(){
        driver.quit()
    }
}
As you can see I removed the lazy dataProvider as I was unable to pass the object correctly to the factory. The main issue I had was when passing the object to a static variable the factory recreate the class with the last loaded static value. Hope this helps you get a dataProvider with factory working correctly for you!
Justin

Intro to web automation with Selenium WebDriver and TestNG inside IntelliJ IDEA

After many painstaking weeks of crawling wiki’s, documentation, and websites trying to figuring out how selenium and testng work together. I decided to write up a tutorial that covers some of the basics.

Selenium Webdriver:

When you first start working with Selenium it can be pretty confusing. The first thing you’ll want to familiarize yourself with is the 3 different versions, Selenium IDE (a Firefox plugin), Selenium Remote Control (Selenium 1 or Selenium RC), and Selenium WebDriver (Selenium 2 or just webdriver). The Selenium website has tons of documentation covering what Selenium is and isn’t, and what each version does. For the purpose of this tutorial we will be working with Selenium WebDriver aka Selenium 2. You have the option of downloading either the SeleniumServer.jar or SeleniumClient.jar from the downloads page. Choose which one best suits your needs.

TestNG:

Next we’ll go over TestNG. Shamelessly stolen from their site, “TestNG is a testing framework inspired from JUnit and NUnit but introducing some new functionalities that make it more powerful and easier to use.” If you’ve never worked with a unit testing framework before this may sound Greek to you. Basically TestNG allows you to add annotations before your classes and methods. This allows you create classes and methods as tests, dataProviders which pass data to our tests, BeforeClass, BeforeTest, BeforeMethod to setup tests, or AfterClass, AfterTest, AfterMethod to tear down our tests! If this doesn’t make much sense right now don’t worry we will cover it later on.

IntelliJ IDEA

The last thing you’ll need to get is a Java IDE. In this tutorial I’m going to be using IntelliJ’s IDEA. If your more comfortable with Eclipse or any other IDE feel free to follow along.

Getting Started: Set up

The first thing you’ll want to do is create a new project from scratch in IDEA

Name your project and make sure ‘Java Module’ is selected then click Next

Leave ‘Create source directory’ checked and click Next

Your now given the option to use Groovy code. NOTE: I usually use Groovy as it’s quicker to code. However if your plan is to compute complex calculations, Groovy becomes very sluggish in this area.

Select if your going to use Groovy and click Finish

Now that you have a project to work with right click on your ‘src’ file and create a new Groovy or Java class.

Name your new class and click OK

Now that we have our first class we need to add the webdriver and testng .jar files to our project. To do that click File -> Project Structure

Inside the Project Structure dialog choose Libraries under Project Settings and click the + icon then Java

Navigate to the location of your selenium or testng .jar file, select it, and click OK

Click OK in the following Choose Modules window

Repeat to add 2nd .jar file to project

Your Libraries should now contain both selenium and testng

Congrats! You are now ready to start building automation using Selenium WebDriver and TestNG.

To continue Read Part 2