I just discovered selenium - a great tool!
I plan to run/use selenium-ide generated junit4 code. But I need it to run with many browsers/web drivers.
Is there a junit/java-pattern for this use case? My first idea was to use #RunWith(Parameterized.class) and provide a List of WebDrivers (the parameter for the class - probably provided as an external file listing browsers and versions?!). Is this a good idea? Is it possible to provide a central #Parameters -method to be used by all my Selenium-tests?
What alternatives are there?
Probably it is possible to change the "Format" that Selenium exports to minimize manual changes?
Well, I do need to switch drivers from time to time, so I did this:
I initialize selenium related stuff in my own Class - called by name of the application and the driver is approached by the getters. When calling my class constructor, I use enum type of driver to initialize with:
private WebDriver driver;
public TestUI(Environment.DriverToUse drv){
switch (drv){
case CHROME:{
ChromeDriverService service = ChromeDriverService.createDefaultService();
File file = new File(TestUI.class.getResource("/chromedriver.exe").toURI());
System.setProperty(ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY, file.getAbsolutePath());
ChromeOptions options = new ChromeOptions();
options.addArguments("--start-maximized");
driver = new ChromeDriver(service,options);
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
break;
}
case FIREFOX:{
FirefoxProfile ffProfile = new FirefoxProfile();
ffProfile.setPreference("browser.safebrowsing.malware.enabled", false);
driver = new FirefoxDriver(ffProfile);
driver.manage().window().setPosition(new Point(0, 0));
java.awt.Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension dim = new Dimension((int) screenSize.getWidth(), (int) screenSize.getHeight());
driver.manage().window().setSize(dim);
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
break;
}
public WebDriver getDriver(){
return driver;
}
of course my Environment class looks like this
public class Environment {
public enum DriverToUse {FIREFOX, CHROME};
// .. and some other stuff, because I need to test on different environments, so I store here Environment URL for example
And my test class looks something like this
#Before
public static final Environment.DriverToUse USED_DRIVER = Environment.DriverToUse.FIREFOX;
#Test
public void testVersionNumber() throws Exception{
TestUI testUI= new TestUI(USED_DRIVER);
WebElement version = testUI.getDriver().findElement(By.id("the Id of element"));
version.click();
//...
}
Use Selenium RC/Selenium Server. These come with the API's you will need to run remote tests in multiple browsers simply. Happy Hunting!
Check out the Selenide library. It's an open source wrapper for selenium that makes UI testing a breeze. Here's an example test.
#Test
public void userCanLoginByUsername() {
open("/login");
$(By.name("user.name")).setValue("johny");
$("#submit").click();
$(".loading_progress").should(disappear); // Waits until element disappears
$("#username").shouldHave(text("Hello, Johny!")); // Waits until element gets text
}
Related
I'm learning automation testing in Java using Selenium and Testng. I've came across the issue that I cannot really solve, maybe someone here knows the solution.
I'm trying to run multiple tests from one class using multisession.xml. That works only if I add separate instance to each test, something like this:
System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe"); WebDriver driver; driver = new ChromeDriver();
However, I want to use separate class (test base) that extends my class with tests in order to not duplicate code. If I do that, then my multisession.xml can only perform one test at the time and returns following exception:
org.openqa.selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document
My guess there is a problem with instances of webdriver. In my base initialization code looks like that:
public class TestBase {
public static WebDriver driver;
public static WebDriverWait wait;
public static Properties config;
public static Properties testdata;
Then setting up separate files with test data and initialization:
public static void initialization() {
String url = config.getProperty("URL");
String browser = config.getProperty("browser");
String pageLoadTimeout = config.getProperty("pageLoadTimeout");
String windowsMaximize = config.getProperty("windowsMaximize");
String deleteAllCookies = config.getProperty("deleteAllCookies");
String waitTimeout = config.getProperty("waitTimeout");
switch (browser) {
case "chrome":
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") +
"/src/main/resources/chromedriver.exe");
ChromeOptions options = new ChromeOptions();
options.addArguments("--incognito");
driver = new ChromeDriver(options);
break;
case "firefox":
System.setProperty("webdriver.gecko.driver", System.getProperty("user.dir") +
"/src/main/resources/geckodriver.exe");
driver = new FirefoxDriver();
break;
case "msedge":
System.setProperty("webdriver.edge.driver", System.getProperty("user.dir") +
"/src/main/resources/msedgedriver.exe");
driver = new EdgeDriver();
break;
case "opera":
System.setProperty("webdriver.opera.driver", System.getProperty("user.dir") +
"/src/main/resources/operadriver.exe");
driver = new OperaDriver();
break;
I will appreciate any help!
StaleElementReferenceException is thrown when the element you're looking for is no longer attached to the DOM. Most probably you're trying to .findElement first, then DOM gets refreshed and you're trying to access it. This often happens with parallel tests execution if your page-class instances with elements are not thread-safe.
Try to use:
object _lock = new object();
lock (_lock)
{
//your driver initialization/login
}
You should also define how many threads will be executing your tests by modifying thread-count="5" parallel="methods"
Can someone please help me to figure out how to set up saucelabs for testNg on Java?
I’ve tried different guides, and it’s always different variations of set up, here's one of the examples that didn't work for me
#BeforeClass
public void init() throws MalformedURLException {
MutableCapabilities sauceOpts = new MutableCapabilities();
sauceOpts.setCapability("username", "oauth-xxxxxx.yyyyyy-51awdfsa");
sauceOpts.setCapability("accesskey", "xxxxx-32b1-4c4d-ac70-yyyyyyyyy");
DesiredCapabilities options = new DesiredCapabilities();
options.setCapability("sauce:options", "sauceOpts");
options.setCapability("browserVersion", "latest");
options.setCapability("platformName", "windows 10");
options.setCapability("browserName","chrome");
Webdriver driver = new RemoteWebDriver(new URL("https://ondemand.us-west-1.saucelabs.com:443/wd/hub"), options);
}
import com.saucelabs.saucebindings.testng.SauceBaseTest;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* Example Test for running with the TestNG Sauce Bindings library.
*/
public class SauceBindingsTestngTest extends SauceBaseTest {
#Test
public void correctTitle() {
getDriver().navigate().to("https://www.saucedemo.com");
Assert.assertEquals("Swag Labs", getDriver().getTitle());
}
}
Chrome/Win10 is the default so no setup code is required. If you want to change the configuration, check the Sauce Bindings docs below.
Source Code for example
We have a Demo Java repo with any example you want, just browse the table of contents
We use Sauce Bindings to make the code so simple
This is working for me,
DesiredCapabilities DC = DesiredCapabilities.chrome();
DC.setBrowserName("chrome");
DC.setPlatform(Platform.WIN10);
driver = new RemoteWebDriver(new URL("https://oauth-jayanth_balakrishnan-ae.saucelabs.com:443/wd/hub"), DC); //http://127.0.0.1:4444/wd/hub
I was hoping for some help setting up my test frame work as its causing me issue. Im currently using zalenium in conjunction with my selenium tests. Currently im setting the desired capabilities in the #BeforeTest section of my tests:
#BeforeTest
#Parameters("browser")
public void setup(String br)throws MalformedURLException {
de = new DesiredCapabilities();
if (br.equals("chrome")) {
de.setCapability(CapabilityType.BROWSER_NAME, BrowserType.CHROME);
de.setCapability(CapabilityType.PLATFORM_NAME, org.openqa.selenium.Platform.LINUX);
}
else if(br.equals("firefox")){
de.setCapability(CapabilityType.BROWSER_NAME, BrowserType.FIREFOX);
de.setCapability(CapabilityType.PLATFORM_NAME, Platform.LINUX);
}
URL url = new URL("http://localhost:4444/wd/hub");
driver = new RemoteWebDriver(url,de);
driver.get(URL);
This allows me to run my testing in the docker environment and not on my local machine and is working correctly.
However i would like to create a base for these capabilities so i don't have to keep stating the desired capabilities for each test.
I want to do this also because I would like to set up separate classes for each page. Currently when i try this im getting a null pointer exception because the driver isnt declared. I tried to inject the Remote webdriver like so:
#Test
public void loginTest( RemoteWebdriver driver){
WebElement user_account_menu = driver.findElement(By.cssSelector("[data-user-account-settings-button]"));
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.elementToBeClickable(user_account_menu));
user_account_menu.click();
System.out.println("Login clicked successfully");
}
Im receiving the error: Cannot inject #Test annotated Method [loginTest] with [class org.openqa.selenium.remote.RemoteWebDriver
So im basically trying to figure out how i can set up these capabilities for the driver in a class and then extend them onto my tests.
From this link, it appears, you're not adding the right annotation, namely #Parameters.
However, in some other testing frameworks, it's not typical to be able to pass in variables that aren't determined at runtime, i.e. variables for objects like RemoteWebdriver won't work, but variables for strings or ints will work. From this link, it appears that what you're trying to accomplish is doable. But I recommend the following approach.
Have an enumeration:
enum BrowserType
{
Chrome, Firefox, IE;
}
Have your test base page:
public class BrowserSetup
{
public static RemoteWebdriver Initialize(string browserType)
{
switch (browserType)
{
case BrowserType.Chrome:
// set chrome capabilities and initialize your browser here
break;
case BrowserType.Firefox:
// set firefox capabilities and initialize your browser here
break;
default:
// set default case, or fail if browser type doesn't match
break;
}
}
Then, from you're test class, assuming you have a private RemoteWebdriver driver initialized somewhere in the test class, you can do the following:
#BeforeTest
#Parameters("Chrome")
public void setup(String browserType) throws MalformedURLException
{
driver = BrowserSetup.Initialize(browserType);
}
I am stuck whilst trying to run a selenium cucumber java script in two browsers (Chrome, Firefox). The test works fine when I use a single browser. I use selenium PageFactory class to initialise the web elements.
The problem is when I run the test for two browsers, the first browser opens, navigates to the URL and then nothing happens. Web elements are not initialised. The test moves on to the second browser, navigates to URL, web elements are initialised, subsequent test methods (testMethod1) run as expected. Why is the test not running on the first browser?
This is the PageFactory BasePage class holding the web elements:
public class BasePage {
private final WebDriver driver;
public BasePage(WebDriver driver) {this.driver = driver;} //constructor
#FindBy(id = "cc-amount")
public WebElement amountField;
This is the test class and how I have tried to run the test in two browsers:
public class Convert {
private static WebDriver driver;
private final BaseUtil baseUtil = new BaseUtil();
private static BasePage basePage;
private static int browser;
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
if (browser == 1) {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
} else if (browser == 2) {
WebDriverManager.firefoxdriver().setup();
driver = new FirefoxDriver();
}
driver.get(baseUtil.getMyUrl()); //Url coming from a utility class
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
basePage = PageFactory.initElements(driver, BasePage.class); //initialisation of the BasePage class conttaining the pagefactory web elements
After the browser initialisation, this method runs ok in the second browser (Firefox) but did not run at all in the first browser (Chrome):
public void testMetod1(String amount) throws InterruptedException {
basePage.amountField.click();
My suspicion is that PageFacory could not be initiated for both browsers in a single run but I do not know how to diagnose this further. It could also be a for loop error.
I believe it's a synchronisation issue. Just you need to create a class for each browser, then you can create another base class which contains all the shared code.
Creating a class for each browser will give you some advantages:
Cleaner code.
Your tests will run in parallel.
Specific test cases for a specific browsers (Specially for IE).
Solve this sync issue.
So basically instead of using Converter, you can create:
ChromeConverter
FirefoxConverter
And inside your TestExecuter class, just create instance for each one and run your tests.
And to be more detailed, you are using one class Convert and inside it you have two static variables:
private static WebDriver driver;
private static BasePage basePage;
Removing the static keyword from BasePage class won't solve the root cause as you are using BasePage which is a static class and can't be instantiated as instance class and it's inside an external library so you need to separate the classes as kind of wrapper.
Here you are trying to use the same exact initialisation with Chrome then Firefox which won't work in parallel or even in for loop until you can wait to the first test then dispose it and initialise all the stuff related to the second test again.
So the correct approach is to create a separate Convert for each browser like that:
ChromeConverter
FirefoxConverter
And In your TestExecuter you just need to initialise each one and use it, something like that:
#Given("...")
public void navigateToUrl(String url) throws InterruptedException {
chromeConverterApp.navigateToUrl(url);
firefoxConverterApp.navigateToUrl(url);
}
#When("^..")
public void enterCurrencies(String amount, String from, String to) throws InterruptedException {
chromeConverterApp.enterCurrencies(amount, from, to);
firefoxConverterApp.enterCurrencies(amount,from,to);
}
The for loop opens Chrome on the first run (browser=1), then opens Firefox on the second run (browser=2) and then continues with the test on the last opened browser (Firefox).
If you wish to run the complete test on both browsers, you need to put the rest of the test inside of the for loop.
Basically, you have something like:
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
//body of the for loop
}
//some more code
} //end of navigateToUrl
You need to change it to:
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
//body of the for loop
}
//some more code
testMethod1()
} //end of navigateToUrl
Your approach is not correct, if you need to run your tests in parallel you should go for either Selenium Grid or consider implementing other multi-threading option, i.e.:
Plain Java: ExecutorService
JUnit or TestNG: Maven Surefire Plugin Fork Options and Parallel Test Execution
In any case you will need to refactor your test to follow Parallel Tests - Best Practices at least:
Remove static modifier from the WebDriver declaration
Consider putting your WebDriver instance into the Thread Local Storage otherwise you will have clashes
I have selenium.properties in which i specify the test configuration (baseURL, browser etc). This is used by ant script to kick off webdriver junit test cases. Now I have few junit test methods, that i want to run only on Firefox. I was wondering if there a way i can accomplish this using JUnit annotations? can i create custom annotations?
my setup
public class TestBase{
public static String baseURL = null;
public static String browser = null;
#BeforeClass
public static void webdriverSetUp() {
try {
FileInputStream fn = new FileInputStream(SELENIUM_PROP_FILE);
Properties selenium_properties = new Properties();
selenium_properties.load(fn);
baseURL = selenium_properties.getProperty("baseUrl");
browser = selenium_properties.getProperty("browser");
} catch (Exception e) {
e.printStackTrace();
}
if(browserg.equalsIgnoreCase("firefox")){
File profileDirectory = new File("./profile");
FirefoxProfile profile = new FirefoxProfile(profileDirectory);
driver = new FirefoxDriver(profile);
}
}
//Test Class
public class TestCase1 extends TestBase{
#Test //run this case only if browser = firefox
public void test1(){
}
#Test //do not run this case if browser = chrome
public void test2(){
}
}
any pointers?
You can easily do this with JUnit with your own runner. In fact, there is a similar working code in Selenium WebDriver test - it's just backwards. The Selenium guys wanted to skip some tests for particular browsers, so they introduced a custom #Ignore annotation.
Take a look at JUnit4TestBase, SeleniumTestRunner and finally TestIgnorance.
You can use their idea to make the opposite and only run the tests with the desired drivers. However, I think you'll need to write it yourself as I am not aware of any good solution out there.