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