简体   繁体   中英

Selenium testing on different browsers using TestNG

So currently I am doing something like this to do cross browser testing:

@DataProvider(name="foo")
public Object[][] getDrivers() {
    DesiredCapabilities firefoxCapabs = DesiredCapabilities.firefox();
    capabillities.setCapability("version", "26");
    capabillities.setCapability("platform", Platform.WINDOWS);
    DesiredCapabilities chromeCapabs = ....
    ....
    DesiredCapabilities ieCapabs = ...
    ....

    return new Object[][]{
        {new RemoteWebDriver(url, firefoxCapabs)}, 
        {new RemoteWebDriver(url, chromeCapabs)},
        ......
    };
}

@Test(dataProvider="foo")
public void testSomething(WebDriver driver) {
    //some test
}

This seems extremely inefficient as I am basically creating and destroying these WebDriver objects every time I run a test. Is there no way to do something like this at least at the TestSuite level so that I am not generating and destroying these objects for every test. I would like something like below. I am aware that you cannot have a DataProvider for @BeforeSuite methods!

public class TestSuite{
    public static WebDriver driver;
    @BeforeSuite(dataProvider="foo")
    public void setDriver(WebDriver driver) {
         this.driver = driver;
    }
}

public class TestClass {
    private WebDriver driver;

    @BeforeTest
    public void getDriver() {
        this.driver = TestSuite.driver;
    }    

    @Test
    public void myTest() {
        //use this.driver to do testing stuff
    }
}

Are there options I am not seeing to do something like this?

Sauce Labs On Demand has a great plugin for Jenkins ( https://saucelabs.com/jenkins/5 ). Their approach is pretty simple: you check/uncheck what OSs and browsers you to test and Jenkins sets environment variables for your tests to pick up. Below is a complete example of using Spring's @Configuration:

package com.acme.test;

import java.net.MalformedURLException;
import java.net.URL;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.env.Environment;

@Configuration
public class SauceLabsWebDriverConfiguration {

    @Autowired private Environment environment;

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public WebDriver webDriver() throws MalformedURLException {
        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability("version", environment.getProperty("SELENIUM_VERSION", "17.0.1"));
        capabilities.setCapability("platform", environment.getProperty("SELENIUM_PLATFORM", "XP"));
        capabilities.setCapability("browserName", environment.getProperty("SELENIUM_BROWSER", "firefox"));
        String username = environment.getProperty("SAUCE_USER_NAME", "enter_your_username_here");
        String accessKey = environment.getProperty("SAUCE_API_KEY", "enter_your_api_here");
        return new RemoteWebDriver(new URL("http://" + username + ":" + accessKey + "@ondemand.saucelabs.com:80/wd/hub"), capabilities);
    }
}

Sauce Labs has some free plans, but if you don't want to use them, you should be able to switch out the last part that constructs the URL ("http://" + username + ":" + accessKey + "@ondemand.saucelabs.com:80/wd/hub") the actual server URL you want to point to (" http://mydomain.com ").

The trick is basically to replace hard-coded browser/capability names with environment provided ones and then have your build runner (ant/maven/etc) set environment variables for each of the OS/browser combos you want to test and "loop" over those somehow. SauceLabs plugins just makes it easy to do the looping. You can still provide default fallback values in case you want to run a simple local test.

// Before
DesiredCapabilities firefoxCapabs = DesiredCapabilities.firefox();
capabillities.setCapability("version", "26");
capabillities.setCapability("platform", Platform.WINDOWS);

// After 
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("version", environment.getProperty("SELENIUM_VERSION", "17.0.1"));
capabilities.setCapability("platform", environment.getProperty("SELENIUM_PLATFORM", "XP"));
capabilities.setCapability("browserName", environment.getProperty("SELENIUM_BROWSER", "firefox"));

Hope it helps.

Two ways to implement this

1) Since you are using a Suite, you can create two tests that will run at the start of the suite and the end , one will setup Webdriver and store it in a global value; which each test class can later access , and the second will close the webdriver.

2) You can use dependency injection to setup webdriver as a singleton/global value and inject it to each test http://testng.org/doc/documentation-main.html#dependency-injection

Here's something you can try: Use the ITestListener. Implement the onStart and onFinish methods to create threadlocal variables for your driver based on the param value ie In onStart(context), fetch your parameter value using

context.getCurrentXmlTest().getParameter("something")

Depending on the param value populate the threadlocal value with appropriate initialized driver

ThreadLocal<WebDriver> threadDriver = new ThreadLocal<WebDriver>()

In your xml pass the browser as parameter for individual test tags and set parallel=tests

<test name="1" >
    <parameter name="browser" value="ff"></parameter>
        <classes>
            <class name="com.nv.test.testngtests.Eq"/>
        </classes>
    </test>
     <test name="2" >
    <parameter name="browser" value="chrome"></parameter>
        <classes>
            <class name="com.nv.test.testngtests.Eq"/>
        </classes>
    </test>

You can take it at suite level too with ISuiteListener, but I think with the test approach you atleast get a chance to have some kind of parallelism

Two ways I see to fix this problem:

  1. Have a different instance of the driver for each class, and use a @BeforeClass to create the driver (I personally do this, because it allows me to run all of my classes in parallel)
  2. Construct your driver in your class's constructor, and use a @Factory .

Both of these solutions will create a driver per class. If you want to have a single set of drivers for your entire suite, create and store an Object[][] statically, and use it in your @dataProvider .

What I have did in my framework is to use configuration file(java properties file) to to set the browser parameter. in your project you can create different classes for Browsers for example IE,Firefox etc.. inside this classes let have the functions to create webdriver with desired capabilities.

Now create a Driver class in which you can create function to initilize webdriver from your browser class as per the input from your proerties file. for example.

you can save properties file with Framework.properties name and inside the file add line like below

    Browser=Firefox
    'Browser=internetExplorer
...so on

To read this property file use java.util.Properties like below.

Properties prop = new Properties();
FileInputStream stream = new FileInputStream("path to property file");
prop.load(stream);
String browser = prop.getProperty(attribute);
stream.close();

Now use this browser parameter in your Driver class as below.

switch (Browser) {
        case "FireFox":
            return FireFox.initializeFireFoxDriver(); 
        case "InternetExplorer":
            return IE.initializeInternetExplorerDriver();                       
        case "Chrome":
            return Chrome.initializeChromeDriver();
        case "Safari":
            return Safari.initializeSafariDriver();
        default:
            break;
        }

Call this class method in your fixture setup and it will start browser instance as per mentioned in properties file.

In case if you don't want to use property file method please use @parameter option in testng.xml for running test suite for passing browser name.

Assuming you are making use of CI, so you can maintain a configuration file that maintains what browser, base URL you should intake for performing a test over test suite.

This configuration can be maintained in a properties file that is always read in @BeforeSuite and accordingly you can load the WebDriver with DesiredCapabilities

Now being in a CI, you can have multiple projects differentiating browser and each can be run parallel/simultaneously, helping you use the resources efficiently and each project will override the configuration file with the required data.

Have a look to Selenium Grid. Never used myself but as far as I understand your question it matches your needs. Selenium Grid allow you to run your selenium test case on different OS/browser combination.

More info on http://docs.seleniumhq.org/docs/07_selenium_grid.jsp and http://code.google.com/p/selenium/wiki/Grid2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM