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
i.e.
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:
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)
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
Related
I'm using Selenium with Java WebDriver and with test Runner Suit(XML), I have 2 tests to run.
I have put a setup method in each Test class and it was OK - the tests was working well (each test with its browser window).
Then I decided to move the setup method to the configuration class. This class is extended by each test class, and this creates a problem that the second test run overrides the first by using the same browser.
Setup class method code:
public class Configrations_And_ScreenShotsFunc_POM {
protected WebDriver driver;
public void setup()
{
System.setProperty("webdriver.edge.driver","C:\\Program Files (x86)\\Microsoft Web Driver\\MicrosoftWebDriver.exe");
driver = new EdgeDriver();
driver.get(URL);
}
Test 1 class Code:
public class TestCase1_POM extends Configrations_And_ScreenShotsFunc_POM {
#BeforeTest
public void Begain() throws InterruptedException
{
setup(); //Setup Browser
}
Test 2 class Code:
public class TestCase2_POM extends Configrations_And_ScreenShotsFunc_POM {
#BeforeTest
public void Begain() throws InterruptedException
{
setup(); //Setup Browser
}
Runner.Xml:
<?xml version="1.0" encoding="UTF-8"?>
<suite name="TestSuite" thread-count="2" parallel="tests">
<test name="TestCase1">
<parameter name="browser" value="Edge" />
<classes>
<class name="POM.Tests.TestCa se1_POM"></class>
</classes>
</test>
<test name="TestCase2_POM">
<parameter name="browser" value="Edge" />
<classes>
<class name="POM.Tests.TestCase2_POM"></class>
</classes>
</test>
</suite>
Comment: The 2 tests files are in a folder, and the configurations class in another folder.
How can it be solved?
Use #BeforeTest annotation on your setup() method instead of your current Begin() and remove your Begin().
The annotation #BeforeTest is intended to be invoked only once per <test> tag in your suite.xml file. Resorting to browser instantiations in a #BeforeTest method which is available in a base class from which all test classes extend has the below disadvantage :
Depending on how the WebDriver instance is being saved (either as a static data member or as an instance variable), test methods can either end up using the same WebDriver instance (in the case of static data members) (or) end up getting NullPointerException (in the case of instance data members)
You can consider moving the browser instantiation into a much more granular level such as :
#BeforeClass (here also the drawback is there that if there are more than one #Test annotated test methods in your class which all use the WebDriver instance initialized by your #BeforeClass, then during parallel execution you will end up having race conditions amongst your test methods ) or into a
#BeforeMethod annotated method.
I had created a blog post that shows you how to do parallel executions with TestNG without using any of these configuration annotations, inheritance etc., Please see if it helps you.
Blog link : https://rationaleemotions.wordpress.com/2013/07/31/parallel-webdriver-executions-using-testng/
I'm using Selenium-serenity for my integration test. By default selenium integrate FirefoxDriver for the WebDriver, but now what I'm trying to do is use PhantomJS. I could not find so far how to set the driver properly after being initialized.
So far what I did is override the getDriver() method of pageObject and return the phantomJs webDriver
private static WebDriver webDriver;
#Override
public WebDriver getDriver() {
if (webDriver == null) {
DesiredCapabilities caps = new DesiredCapabilities();
caps.setJavascriptEnabled(true);
caps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, "/Users/pabloperezgarcia/Downloads/phantomjs");
webDriver = new PhantomJSDriver(caps);
setDriver(webDriver);
}
return webDriver;
}
But the problem is that every single action over webdriver is not propagate over the other pages because of course we are only returning the singleton webDriver, but not the super.getDriver().
My question is how can I set properly the webdriver on serenity to be share the state between multiple pages object, which are new instances.
With Firefox seems work perfectly.
You need to set the webdriver.driver property to phantomjs. With serenity this can be passed via command line, defined in a properties file, or annotated in the code. You may also need to specify the location of the phantomjs driver via a system property -Dphantomjs.binary.path=path/to/driver.
You mentioned you're using annotations, have you tried #Managed(driver="phantomjs")?
You could also pass via command line (in your IDE it would be in run configuration) -Dwebdriver.driver=phantomjs
Be aware if you are running your tests remotely you may also need to set the phantomjs.webdriver property to the port you want to run on.
You can also set properties via maven:
<properties>
<webdriver.driver>phantomjs</webdriver.driver>
</properties>
and then in your failsafe plugin define the system property
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.18</version>
<configuration>
<systemProperties>
<webdriver.driver>${webdriver.driver}</webdriver.driver>
</systemProperties>
</configuration>
</plugin>
If you want to define a custom driver provider, you need to make sure you implement DriverSource and define the following properties webdriver.driver,
webdriver.provided.type, webdriver.provided.mydriver, thucydides.driver.capabilities
Serenity documentation: http://thucydides.info/docs/serenity-staging/
I have been working on TestNG and there was a problem associated with some of my code. Here's the code:
public class Main {
public String baseurl ;
public WebDriver webdriver ;
protected Main(){
//baseurl = "http://goodreads.com";
//webdriver = new FirefoxDriver();
}
#BeforeSuite
public void setup(){
baseurl = "http://goodreads.com";
webdriver = new FirefoxDriver();
}
Above code is of the base class
#Test
public void do_login(){
super.webdriver.get(super.baseurl);
This is of inherited class
Now as far as I know the BeforeSuite should executed first and the values of baseurl and webdriver should get initialized to the specified values. But I'm getting a NullPointerException in the above code. The problem is resolved when I assign the variables as static or if I initialize the variables in the constructor. But why are they not being initialized when I put them in the method(which is supposed to execute before the #Test executes anyway)? Is there some concept about java that I'm missing? Please explain
You don't need to include both classes to the suite. Only #Test containing class should be included. I think that's the problem. I also believe that #BeforeSuite annotated method will be executed not depending on the way you run your tests (as TestNG method or TestNG suite), since default suite will be created in order to run tests anyway.
Could you please look at my problem and give any advice to its solving.
I use JUnit4 and selenium 2 WebDriver.
So, I have class to run JUnit suite:
#RunWith(Suite.class)
#Suite.SuiteClasses({className1.class, clasName2.class})
public class TestSuite
{
public static TestSuite suite()
{
TestSuite suite = new TestSuite();
suite.addTest(new JUnit4TestAdapter(className1.class));
suite.addTest(new JUnit4TestAdapter(className2.class));
return suite;
}
}
each class contains #Test method and extends BaseClass that sets in #BeforeClass parameters (through DesiredCapabilities) to run suite on BrowserStack machines:
public class MyTestBase{
static protected WebDriver driver;
private boolean acceptNextAlert = true;
protected static StringBuffer verificationErrors = new StringBuffer();
#BeforeClass
public static void setUp() throws Exception {
DesiredCapabilities capability = DesiredCapabilities.firefox();
capability.setPlatform(Platform.WINDOWS);
capability.setCapability("build", "JUnit - Sample");
capability.setCapability("acceptSslCerts", "true");
capability.setCapability("browserstack.debug", "true");
driver = new RemoteWebDriver(
new URL("http://username:accesskey#hub.browserstack.com/wd/hub"),
capability);
driver.manage().timeouts().implicitlyWait(40, TimeUnit.SECONDS);
}
/* other code */
}
So, could you please help me with the next:
1) I need to create configuration file and use its parameters to run my TestSuite on different browsers in BrowserStack. Any examples of .xml file to do it will be appreciated.
2) And also how do I need to modify my TestSuite.class to use .xml file parameters.
3) My TestSuite.class consists of many .class with #Test method in each. Each class extends MyTestBase.class where annotations #BeforeClass and #AfterClass are located, but when I run TestSuite new browser has been launched for each class in TestSuite and it's a very big problem for me. What can I do for running browser once for all #Test methods across all classes in TestSuite. I know that #BeforeClass works for all #Test methods inside one class, but what should be done if there are may classes? In TestNG there is #BeforeSuite solves this problem.
Sorry, for so many questions, but I've tried a lot and didn't succeed in this :(
Thanks a lot!
You asked a lot of questions concerning different topics. I will try to sort things out.
Reusing a browser and not opening a new one for every test class
This can only be achieved if you instantiate your WebDriver once and use that object in all your tests. So don't instantiate your browser in the #BeforeClass method of your tests.
How do you initialise your WebDriver?
In the very first test class of your test suite. That might be a dedicated test just for opening the browser. Or you could include this functionality in all of your test and have to check, if the browser had already been initialised or not.
How to reuse a WebDriver object and share it between test classes?
I'd propose to create a Singleton that stores a WebDriver object. This way all tests can access it. However, it takes some more effort to make this thread-safe - in case your run your tests in parallel.
Running your tests with different browsers
You should make your tests #Parameterized and expect a set of WebDriver objects as parameters to execute.
Combining both: Different browsers and reusing browsers between tests
This will likely lead to a point where you would like to define parameters for your test suite. However, in standard JUnit 4 you can't do this.
I recommend to use the ParameterizedSuite runner from this library.
Can someone recommend the best way to 'tearDown' in the #AfterClass with testng? I occasionally get a hung window or popup and can't get back to the homepage to logout gracefully for the next class to start and avoid modal dialog errors.
I've tried getting a window handle on the homepage, then one on the popup, then putting a switchTo in my #AfterClass but it doesn't work. I've also tried driver.close(). But then my next class will be skipped with an unreachable browser error.
I really need a good tearDown #AfterClass method that can get out of whatever errors and popups are on page, and just leave a clean browser window at login page for the next test class to run.
Edit: Adding Code:
package TestPackage;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.*;
import org.testng.annotations.Test;
//#Test (priority = 1)
public class test1 {
public static WebDriver driver = new FirefoxDriver();
Environment environment = Environment.QA03();
User testUser = User.ns_system();
AxUtilities axUtilities;
#BeforeClass
#Parameters("environment")
#Test
public void login(String environment1){
// environment = new Environment(environment1);
axUtilities = new DAxUtilities(environment1, driver);
// DAxUtilities dAxUtilities = new DAxUtilities(environment);
Login login = new Login();
login.getLogin(testUser, axUtilities, driver);
axUtilities.sleep(5000);
}
#Test
public void testNothing(){
String s = "public void testNothing() reached...";
System.out.println(s);
}
#AfterClass
public void verifyOKAndLogout() {
driver.quit();
// DAxUtilities dAxUtilities;
}
}
test class 1 and 2 are the same except for the class name...
And the tests are run from xml file. And I've tried many variants of the xml file, even putting each test in its own xml file:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestSuite1" verbose="1">
<parameter name="environment" value="QA03"/>
<test name="Test1">
<classes>
<class name="TestPackage.test1"/>
<!--<class name="TestPackage.test2"/>-->
</classes>
</test>
<test name="Test2">
<classes>
<class name="TestPackage.test2"/>
</classes>
</test>
</suite>
But always two web drivers get created, one right after the other, at the very beginning of the test run, before any other code is executed. And breakpoints show the code is definitely skipping from the beginning of class 1, to the beginning of class 2, before running class 1 tests...
From what you wrote it seems that you are creating browser (instantiating WebDriver) before entire test suite and then execute all the tests using the same browser instance. Although it results with faster execution time it also introduces problems like the one you are having right now.
I would suggest creating a new instance of the browser before executing each test class (or even test method). In such case you can safely use driver.quit() in your #AfterClass method. Restarting browser every test will make your tests much more stable.
EDIT comments on your newly added code
the way you are instantiating WebDriver is not correct. If you have
several test classes you will end up with multiple browser windows
opened before any test is executed
you annotated one method with both #Test and #BeforeClass - this will cause the method to be executed twice in a row
The way it should (more or less) looks like is
public class MyTest {
protected WebDriver driver;
#BeforeClass //I would even suggest to use #BeforeMethod but that's up to you
public void setUp() {
driver = new FirefoxDriver();
//other instantiations and assignments if necessary
}
#Test
public void login() {
driver.get("http://watever.com");
//do whatever
}
#Test
public void someOtherTest() {
//do something else
}
#AfterClass //or #AfterMethod
public void tearDown() {
driver.quit();
}
}
I believe that calling driver.close() or driver.quit() and then creating a new WebDriver and assigning it to your driver should do the trick. You'd probably want to call the same function to create the driver at both the beginning and end of your class.
Use
#tearDown{
driver.quit();
}
It will close all the windows open by test class.
And in the Next class create new instance of Webdriver() as shown below
Webdriver driver = new FireFoxDriver(); //Or some other driver