I am trying to create a framework to test cross-platform mobile web apps (non native). Here is how my code is setup:
Test Class:
public class TestSuite extends MobileLibrary{
#BeforeClass
public static void setUpTests() throws Exception {
setUp();
}
#AfterClass
public static void cleanUpTests() throws Exception {
driver.quit();
}
#Test
public void validateSignIn() throws Exception
{
String username = "testtest";
String password = "testtest";
SignInMobile(driver,username,password);
assertTrue(true);
}
#Test
public void randomTests() throws Exception
dbcSelector(driver,"test");
assertTrue(true);
}
}
Base Class
public class SetupBase
{
protected static AppiumDriver driver;
protected static AppiumPlatform appiumPlatform;
protected static DeviceSize deviceSize;
protected static DeviceName deviceName;
protected static String deviceID;
protected static DesiredCapabilities capabilities;
protected static enum AppiumPlatform{
DESKTOP,IOS,ANDROID
}
protected static enum DeviceSize{
SMALL,MEDIUM,LARGE
}
protected static enum DeviceName{
NEXUS7,S5,IPHONE5S
}
public static void setUp() throws MalformedURLException /*throws Exception*/ {
String deviceNameEnv = System.getenv("DEVICENAME");
switch(deviceNameEnv){
//sets devicename here
}
//sets platform here
if(deviceName==DeviceName.NEXUS7 || deviceName==DeviceName.S5)
appiumPlatform = AppiumPlatform.ANDROID;
else if(deviceName==DeviceName.IPHONE5S)
appiumPlatform = AppiumPlatform.IOS;
else
appiumPlatform = AppiumPlatform.DESKTOP;
switch(deviceName){
//sets size here
}
capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceName",deviceID);
if(appiumPlatform==AppiumPlatform.ANDROID){
capabilities.setCapability("platformName","Android");
capabilities.setCapability("browserName","Chrome");
driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
}
else if(appiumPlatform==AppiumPlatform.IOS){
capabilities.setCapability("platformName","iOS");
capabilities.setCapability("browserName","Safari");
driver = new IOSDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
}
}
}
No matter what I do, any time I try to use driver in the second test I will get a java null pointer exception. MobileLibrary extends the baseclass and contains all my WebDriver element functions.
Edit: If I directly do driver.findelement in my second test I get an element not found exception. It looks like something is being reset after the end of a #test and I have no idea what.
I found out that the problem was actually with the #test order execution. I didn't realize that junit doesn't run tests in order, I'll be looking into the test suite fixtures.
Related
I am new to Selenium, so basically whenever I ran my test, it would open up the URL which I have mentioned in my test. In my same Test I have also mentioned to fill the username and Password.
But somehow once the browser gets launched and redirect to the URL, it opens up another instance of blank browser failing my script that element not found.
Please help me here.
///////////////////////////////////////////////////////////////////////////
public class TruefillTest extends BaseClass {
public Truefill truefill()
{
WebDriver driver=InitializeDriver();
return new Truefill(driver);
}
#Test
public void userLoginIntoTheSystem()
{
truefill().dashBoard().Url();
truefill().dashBoard().EnterUsername("bjdgfe#swcwr.com");
truefill().dashBoard().EnterPassword("Test1234");
}
///////////////////////////////////////////////
public class Truefill {
private WebDriver driver;
public Truefill(WebDriver driver) {
this.driver=driver;
}
public DashBoardPage dashBoard()
{
return new DashBoardPage(driver);
}
////////////////////////////////////////////////////////////
public class DashBoardPage {
private final WebDriver driver;
By Username= By.xpath("//input[#name='name']");
By Password= By.xpath("//input[contains(#id,'exampleInputPassword1')]");
public DashBoardPage(WebDriver driver) {
this.driver=driver;
}
public void Url()
{
driver.get("https://rahulshettyacademy.com/angularpractice/");
}
public void EnterUsername(String username)
{
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
driver.findElement(Username).sendKeys(username);
}
public void EnterPassword(String password)
{
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
driver.findElement(Password).sendKeys(password);
}
////////////////////////////////////////////////////////////
public class BaseClass {
WebDriver driver;
public WebDriver InitializeDriver()
{
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
return driver;
}
}
Each call to the truefill() method initializes a new instance of WebDriver. Since your test is calling it multiple times, it will start a new browser instance on each line. Instead, store the DashboardPage in a local variable:
#Test
public void userLoginIntoTheSystem() {
DashBoardPage dashBoardPage = truefill().dashBoard();
dashBoardPage.Url();
dashBoardPage.EnterUsername("bjdgfe#swcwr.com");
dashBoardPage.EnterPassword("Test1234");
}
You might also want to use a setup method to initialize the Truefill instance, rather than creating it on demand:
private Truefill truefill;
#BeforeEach
public void initializeTruefill() {
WebDriver driver = InitializeDriver();
truefill = new Truefill(driver);
}
#Test
public void userLoginIntoTheSystem() {
DashBoardPage dashBoardPage = truefill.dashBoard();
dashBoardPage.Url();
dashBoardPage.EnterUsername("bjdgfe#swcwr.com");
dashBoardPage.EnterPassword("Test1234");
}
This assumes JUnit 5. If you're using JUnit 4, the annotation is #Before instead of #BeforeEach.
I have 3 classes like below but when I try to run my test I get NullPointer (TestPage->input1). On debug I spotted that I have a second instantion of driver which is null. Can anyone help me what I did wrong and how to fix it? Shouldn't it work properly?
public class BaseScenario {
protected WebDriver driver;
#BeforeMethod
public void setUp() throws Exception {
System.setProperty("webdriver.chrome.driver", "C:\\Users\\Ed\\Desktop\\chromedriver.exe");
driver = new ChromeDriver();
driver.get("http://toolsqa.com/automation-practice-form/");
}
#AfterMethod
public void TearDown() {
driver.quit();
}
}
'
public class TestsPage {
WebDriver driver;
public TestsPage(WebDriver driver)
{
this.driver=driver;
}
public void input1(){
driver.findElement(By.xpath("//*[#id='content']/form/fieldset/div[1]/input[1]")).sendKeys("Kuba");;
}
public WebElement input2(){
WebElement input2 = driver.findElement(By.xpath("//*[#id='content']/form/fieldset/div[1]/input[2]"));
return input2;
}
}
'
public class Tests extends BaseScenario{
TestsPage pagee = new TestsPage(driver);
#Test
public void TC1() throws Exception {
pagee.input1();
pagee.input2().sendKeys("Chudy");
}
}
I am running parallel tests using selenium (selenium-server-standalone-2.47.1.jar) grid with TestNg, kicked off by ant, and using a TestListenerAdapter. Screenshots are taken in the listener's 'onTestFailure' method. The problem is that the listener seems to get crossed up about which driver it should be using, and sometimes takes a screenshot of the wrong browser window, or fails altogether if the driver that it thinks it should be using has already quit.
When the tests start, TestNg's #BeforeTest and the TestListenerAdapter's 'onTestStart' methods are running on the same thread, but when the test fails, the TestListenerAdapter's 'onTestFailure' method appears to be running on a separate thread. It seems like the threads are getting crossed/shared somehow, but I can't figure out why.
Here is some skeleton code, any assistance greatly appreciated.
Base Test Class:
public class baseClassTests{
protected AutomationUtils au;
protected DriverUtils du;
#BeforeTest(alwaysRun = true)
#Parameters({ "selenium.OS", "selenium.browser" })
public void beforeTest(String OS, String browser) {
//these call simple private methods to know where to set up the driver
String port = getPort(OS, browser);
String host = getHost(OS);
//make a driver utility object here, this makes a driver
du = new DriverUtils(browser, host, port);
//pass this driver utility object to another class of utilities
//this 'AutomationUtils' class gets a RemoteWebDriver ('driver') by calling driver=du.getDriver();
//the 'AutomationUtils' class is then the one that does all of the 'driver.findBy...' etc
au = new AutomationUtils(du);
}
#BeforeMethod(alwaysRun = true)
public void beforeMethod(Method m, ITestResult tr) {
du.deleteCookies();
testNgTestName = m.getName();
print("Method: "+testNgTestName + " Thread: "+Thread.currentThread().hashCode());
//set the attribute of the ITestResult object so we can use the same object in the listener
tr.setAttribute("du", du);
tr.setAttribute("au", au);
}
}
Listener class
public class AmSimpleTestListener extends TestListenerAdapter {
private DriverUtils driveU;
private AutomationUtils AutoU;
private RemoteWebDriver driver;
private RemoteWebDriver augmentedDriver;
private String methodName;
private String browser;
private String browserVersion;
String testClass;
#Override
public void onTestStart(ITestResult tr) {
//pick up the correct driver utility object from the test class/method we are in
driveU = (DriverUtils) tr.getAttribute("du");
AutoU = (AutomationUtils) tr.getAttribute("au");
driver = du.getDriver();
augmentedDriver = (RemoteWebDriver) new Augmenter().augment(driver);
methodName = tr.getName();
testClass=tr.getTestClass(); //sort of, I actually parse it up a bit
browser = driveU.getBrowser();
browserVersion = driveU.getBrowserVersion();
print("Method: "+methodName + " Thread: "+Thread.currentThread().hashCode());
}
#Override
public void onTestFailure(ITestResult tr) {
print("Method: "+tr.getName() + " Thread: "+Thread.currentThread().hashCode());
try{
writeScreenshotFile();
}
catch (Exception e){
Out.error("Unable to take screen shot");
e.printStackTrace();
}
}
private String writeScreenshotFile() {
if (driver != null && driver.getSessionId() != null) {
File scrShot = ((TakesScreenshot) augmentedDriver).getScreenshotAs(OutputType.FILE);
File localPathToScreenShot = new File("/path/to/base/directory/"+testClass+"/"+methodName+".png");
try {
FileUtils.copyFile(scrShot, localPathToScreenShot);
} catch (Exception e) {
Out.error("Couldn't write screenshot to file");
}
return localPathToScreenShot.getAbsolutePath();
}
return "Could not get path.";
}
}
DriverUtils class makes/supplies the driver
public class DriverUtils {
private RemoteWebDriver driver;
private int timeout;
private String browserVersion;
private String browser
private DesiredCapabilities caps;
public DriverUtils(String browser, String host, String port) {
String hostUrl = "http://" + host + ":" + port + "/wd/hub";
this.browser=browser;
//do some stuff here to set capabilties
driver = new RemoteWebDriver(new URL(hostUrl), caps);
browserVersion = driver.getCapabilities().getVersion();
}
public RemoteWebDriver getDriver() {
return driver;
}
public AmBrowser getBrowser() {
return browser;
}
public String getBrowserVersion() {
return browserVersion;
}
public void quitDriver() {
driver.quit();
}
public void deleteCookies(){
driver.manage().deleteAllCookies();
}
}
public class AutomationUtils extends BaseClassUtils {
public AutomationUtils(DriverUtils driverUtils) {
//pass it up to the base class utils (this is different than base class tests, above)
//do this so the driver can be accessed by other utility classes as well
super(driverUtils);
}
//All sorts of methods here to find elements, login, blah blah everything that is done with a driver object
}
public class BaseClassUtils { //this is a different class than BaseClassTests
//make the driver a protected object so all utility classes can access as nec.
protected final RemoteWebDriver driver;
public BaseClassUtils(DriverUtils driverUtils) {
driver = driverUtils.getDriver();
}
}
Tests are run via ant.
<suite name="Dev2 for debugging" parallel="tests" thread-count="10">-- tests here </suite>
After tinkering this for a while, I came to the conclusion that there were two things that seemed to help tremendously.
eliminate the listener, and take all screenshots in the #AfterMethod
Move the #Before/After Method/Test methods into the child classes, but simply call methods in the parent to do all the work.
Another thing I noticed is that for #2, TestNG is supposed to run the parent #Before methods then the child #Before methods; and then at the end run the child '#After' methods and then the parent #After methods.
I ran a series of simple tests, I found that all before/after methods were not being run, so for the few cases where I was using #Before and #After methods in both parent and child, I consolidated.
Things seem to run much better now, the driver does not get confused, and screenshots are being attached to the correct browser/test.
Try using a ThreadLocal for RemoteWebDriver so it can handle parallel runs:
public class DriverUtils {
private static ThreadLocal<RemoteWebDriver> driver = new ThreadLocal<~>();
private int timeout;
private String browserVersion;
private String browser
private DesiredCapabilities caps;
public DriverUtils(String browser, String host, String port) {
String hostUrl = "http://" + host + ":" + port + "/wd/hub";
this.browser=browser;
//do some stuff here to set capabilties
driver.set(new RemoteWebDriver(new URL(hostUrl), caps));
browserVersion = getDriver().getCapabilities().getVersion();
}
public RemoteWebDriver getDriver() {
return driver.get();
}
public AmBrowser getBrowser() {
return browser;
}
public String getBrowserVersion() {
return browserVersion;
}
public void quitDriver() {
getDriver().quit();
}
public void deleteCookies(){
getDriver().manage().deleteAllCookies();
}
}
I created two tests. Pulled up BeforeSuite and AfterSuite in superclass method.
Tests passes individually but fails when I execute them as a package.
Error NullPointerException at string
driver.get(baseUrl + "test/test/");
public class MyTestBase {
private WebDriver driver;
private String baseUrl;
private boolean acceptNextAlert = true;
#BeforeSuite
public void setUp() throws Exception {
driver = new FirefoxDriver();
baseUrl = "http://test.com/";
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
#AfterSuite
public void tearDown() throws Exception {
driver.quit();
}
Code:
private void login() throws InterruptedException {
driver.get(baseUrl + "test/test/");
driver.findElement(By.xpath("//input")).clear();
driver.findElement(By.xpath("//input")).sendKeys("user234");
driver.findElement(By.xpath("//div[2]/div[2]/div/input")).clear();
driver.findElement(By.xpath("//div[2]/div[2]/div/input")).sendKeys("645637");
submitButton();
Thread.sleep(2000);
}
Test 1
`public class AddEquipment extends MyTestBase { }`
Test 2
`public class AddAnotherEquipment extends MyTestBase { }`
Both tests are the same, but with different parameters Equipment and Description fields.
I am at the beginning за learning WebDriver and tried to create something simple.
Code for test 1:
package com.example.tests;
import org.testng.annotations.Test;
public class AddEquipment extends MyTestBase {
#Test
public void testAddEquipment() throws Exception {
GroupData group = new GroupData("Equipment", "Description");
createEquipment(group);
// assert
}
protected void createEquipment(GroupData group) throws InterruptedException {
login();
menuClickEquipment();
clickAddButton();
fillOutForm(group);
submitButton();
logout();
}
private void login() throws InterruptedException {
driver.get(baseUrl + "test/test/");
driver.findElement(By.xpath("//input")).clear();
driver.findElement(By.xpath("//input")).sendKeys("user234");
driver.findElement(By.xpath("//div[2]/div[2]/div/input")).clear();
driver.findElement(By.xpath("//div[2]/div[2]/div/input")).sendKeys("645637");
submitButton();
Thread.sleep(2000);
}
I have been writing Selenium test for web application and there seem to be multiple instances of Internal Server Error in application in case of Internal Server Error, application displays custom error page and and error id is displayed to user to pursue matter with technical team, in case user encounter it.
This makes it a little laborious to debug the test failures during Selenium execution.
I was thinking to use some mechanism to keep polling a page with each step executed in test to find if there was any instance of Internal Server error, And this is when I came across Junit Rule and thought of writing a custom annotation for it, some thing like -
public class SelTestCase {
protected WebDriver driver;
#Before
public void startDriver() {
driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://www.google.com/");
}
#After
public void closeDriver() {
driver.quit();
}
}
public class GoogleSearchTest extends SelTestCase {
#Rule
PageChecker pageChecker = new PageChecker();
#Test
#CheckPage
public void testGoogleSearch() {
GoogleHomePage googleHomePage = PageFactory.initElements(driver,
GoogleHomePage.class);
googleHomePage.searchGoogle("Selenium HQ");
assert driver.getPageSource().contains("seleniumhq") : "Selenium headquarter search failed";
}
}
SelTestCase class creates instance of WebDriver to execute test, And here is the PageChecker class -
public class PageChecker extends SelTestCase {
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
public #interface CheckPage {
// page check should take place here (Though not sure if it is right place)
// like if(driver.getPageSource.contains("Internal Server Error") {throw Exception ("Err")}
}
}
This is what I am stuck with, how do I proceed with CheckPage annonations?
IMHO there are two solutions to your problems. If the feature is only needed by a small part of your tests, then I would not use a rule. Instead add a single line errorChecker.checkPage(driver) to each tests and implement the check in this method.
If you need it for almost all your tests:
Convert SelTestCase to a rule by extending ExternalResource.
public class WebDriverRule extends ExternalResource {
public WebDriver driver;
#Override
protected void before() {
driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://www.google.com/");
}
#Override
protected void after() {
driver.quit();
}
}
Add the page check code to the rule by extending Verifier.
public class PageChecker extends Verifier {
private WebDriverRule webDriverRule;
private enabled = true;
public PageChecker(WebDriverRule webDriverRule) {
this.webDriverRule = webDriverRule;
}
public void disable() {
this.enabled = false;
}
#Override
public void verify() {
if(enabled && notValid())
throw new AssertionError("foo");
}
private boolean notValid() {
WebDriver driver = webDriverRule.driver;
//do something with driver
}
}
Use org.junit.rules.RuleChain to control the execution order of the two rules.
public class GoogleSearchTest {
private WebDriverRule webDriverRule = new WebDriverRule();
private PageChecker pageChecker = new PageChecker(webDriverRule);
#Rule
public RuleChain driverAroundPageChecker
= RuleChain.outerRule(webDriverRule).around(pageChecker);
#Test
public void testGoogleSearch() {
GoogleHomePage googleHomePage = PageFactory.initElements(driver,
GoogleHomePage.class);
googleHomePage.searchGoogle("Selenium HQ");
assert driver.getPageSource().contains("seleniumhq") : "Selenium headquarter search failed";
}
#Test
public void testWithouPageCheck() {
pageChecker.disable();
//here is your real test
}
}