Selenium: How to stop execuing subsequest steps in #Test method after catching an exception using TestNG Listener? - selenium

I am working on PageFactory in Page Object Model and TestNG framework for automation. I am using beforeFindBy method in WebDriverEventListener to listen for the presence of an element. When an element is not present, NoSuchElementException is thrown and I catch the exception using try-catch block. How to stop executing remaining steps in #Test method. I do not want to use try-catch in my #Test method. Any suggestion on changes in my design is most appreciated. Below is my design.
LoginPage
public class LoginPage {
#FindBy(xpath = "//img[#src='/webres_5786257bd7c8a5.72130757/themes/default/images/login/logo.png']")
WebElement loginLogo;
public LoginPage(WebDriver driver, ExtentTest test) {
this.driver = driver;
PageFactory.initElements(driver, this);
this.test = test;
}
public void verifyLoginPageLogo() throws IOException {
Assert.assertTrue(loginLogo.isDisplayed());
test.log(LogStatus.PASS, "Logo is displayed", takeScreenshot(driver, test));
}
}
Listenter
public class CustomListeners extends TestListenerAdapter implements WebDriverEventListener {
//All Implemented Methods
public void beforeFindBy(By by, WebElement element, WebDriver driver) {
try {
driver.findElement(by);
} catch (NoSuchElementException e) {
System.out.println("Element " + by + "not found");
}
}
}
Test class
#Listeners(CustomListeners.class)
public class Test1 extends BaseTestSuite {
LoginPage lp;
TabMenu tm;
#Test(priority = 0, testName = "Verify Login")
public void login() throws Exception {
lp = new LoginPage(driver, test);
tm = new TabMenu(driver, test);
driver.get(Constants.url);
lp.verifyLoginPageLogo(); //Element not present here
lp.setUserName("dmin");
lp.setPassword("admin");
lp.clickLoginBtn();
tm.verifyTabMenu();
tm.isCurrentTab("Dashboard");
}
}

The Login PageObject ideally should not contain assertion code. That code should be in your test class. Also the Extent functionality should also be in the test class and not the PageObject.
There is no need for a testng listener or a webdrivereventlistener. In the PageObject if restructure the verifyLoginPageLogo() method to below -
public boolean isLoginPageLogoDisplayed() throws IOException {
//Write logic to check if element is displayed.
//Return false in case of exception etc
}
In the test class do this -
Assert.assertTrue(lp.isLoginPageLogoDisplayed());
If an ArrertionError is thrown the remaining steps are skipped automatically.
ALso look at creating a BasePageObject which has utility methods for checking presence, displayed, finding elements etc etc. Just extend this.

Related

screenshot listener in selenium with TestNG

I have a testcase that will invoke the driver as a non static variable. I also have added screenshot listener in my test case. When the test case fails The control is automatically sent to the screenshot listener, however since my driver is a NON-STATIC variable it could not be accessed in the screenshot listener. So I get nullpointer exception.
To solve this I created a simple base test class with help of stackoverflow like below and extend it in each testclass
public asbtract baseTest() {
private WebDriver driver;
public WebDriver getDriver() {
return driver;
}
#BeforeMethod
public void createDriver() {
Webdriver driver=XXXXDriver();
}
#AfterMethod
public void tearDownDriver() {
if (driver != null)
{
try
{
driver.quit();
}
catch (WebDriverException e) {
System.out.println("***** CAUGHT EXCEPTION IN DRIVER TEARDOWN *****");
System.out.println(e);
}
}
}
In my listener, I access the base class as follows
public class ScreenshotListener extends TestListenerAdapter {
#Override
public void onTestFailure(ITestResult result)
{
Object currentClass = result.getInstance();
WebDriver webDriver = ((baseTest) currentClass).getDriver();
if (webDriver != null)
{
File f = ((TakesScreenshot) webDriver).getScreenshotAs(OutputType.FILE);
//etc.
}
}
My test class looks like this
#Listeners(value = FailureReport.class)
public class LoginTest extends baseTest{
Login login = new Login(getDriver());
#Test(description = "Verify Expand the Menu", priority = 0)
public void navigateloginpage() {
login.expandMenuScreenLogin();
login.navigateLoginPage();
}
#Test(description = "User login Sucessfully", priority = 1)
public void successLogin() {
login.login();
}
When I run this it gives me the error
org.testng.TestNGException:
Cannot find class in classpath: com.fbf.automation.tests.LoginTest
at org.testng.xml.XmlClass.loadClass(XmlClass.java:81)
at org.testng.xml.XmlClass.init(XmlClass.java:73)
at org.testng.xml.XmlClass.<init>(XmlClass.java:59)
at org.testng.xml.TestNGContentHandler.startElement(TestNGContentHandler.java:548)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:509)
at com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator.startElement(XMLDTDValidator.java:745)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:1359)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2784)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:602)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:505)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:841)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:770)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:643)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl.parse(SAXParserImpl.java:327)
at javax.xml.parsers.SAXParser.parse(SAXParser.java:195)
at org.testng.xml.XMLParser.parse(XMLParser.java:38)
at org.testng.xml.SuiteXmlParser.parse(SuiteXmlParser.java:16)
at org.testng.xml.SuiteXmlParser.parse(SuiteXmlParser.java:9)
at org.testng.xml.Parser.parse(Parser.java:172)
at org.testng.TestNG.initializeSuitesAndJarFile(TestNG.java:302)
at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:45)
at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:123)
Process finished with exit code 0
Later when I changed my test class as follows it ran correctly
#Listeners(value = FailureReport.class)
public class LoginTest extends baseTest{
Login login;
#Test(description = "Verify Expand the Menu", priority = 0)
public void navigateloginpage() {
login = new Login(getDriver());
login.expandMenuScreenLogin();
login.navigateLoginPage();
}
#Test(description = "User login Sucessfully", priority = 1)
public void successLogin() {
login.login();
}
Isn't it possible to declare the code
login = new Login(getDriver());
outside #test method rather than writing it inside the #test method. Or are there anyother alternative ways I can call the above code
You may create Login object inside #BeforeClass method
#BeforeClass(alwaysRun = true)
public void setUp() throws Exception {
login = new Login(getDriver());
}

Null Pointer exception using Page object model with Cucumber

I have used Page Object Model design pattern along with Cucumber. So for this I have created two pages named as abstractPage and loginPage but on running the script i get null pointer exception even when i have already initialize the webelements using pagefactory , please have a look at below code:
abstractPage:
public class abstractPage {
protected WebDriver driver;
public static loginPage lpOBJ;
public void openBrowsernURLhit() {
driver=new FirefoxDriver();
driver.get("http://www.facebook.com");
PageFactory.initElements(driver, loginPage.class);
}
}
loginPage:
public class loginPage extends abstractPage {
#FindBy(name = "email")
public WebElement username;
#FindBy(name = "pass")
public WebElement password;
#FindBy(id = "u_0_2")
public WebElement loginButon;
public void loginIntoApp() {
String url=driver.getCurrentUrl();
System.out.println("the url is::::::::::::::::"+url);
username.sendKeys("testuser");
password.sendKeys("123");
}
public void clicklogn() {
loginButon.click();
}
}
And then I have a class stepDefination from where I am calling the above methods of login class. Browser is opened fine and url is hit but when it goes inside loginintoapp() method it throws exception on first line itself.
public class SmokeTest {
#Given("^Open Firefox and start application$")
public void Open_Firefox_and_start_application() throws Throwable {
abstractPage obj = new abstractPage();
obj.openBrowsernURLhit();
}
#When("^I enter valid \"([^\"]*)\" and \"([^\"]*)\"$")
public void I_enter_valid_username_and_password(String arg1, String arg2) throws Throwable {
loginPage lpobj = new loginPage();
lpobj.loginIntoApp();
}
#Then("^user should be able to login successfully$")
public void user_should_be_able_to_login_successfully() throws Throwable {
loginPage lpobj = new loginPage();
lpobj.clicklogn();
}
}
I also have a TestRunner class where i have glued my stepDefination:
#RunWith(Cucumber.class)
#CucumberOptions(
features= "features",
glue= {"stepDefination"},
plugin= {"html:target/cucumber-html-report"}
)
public class TestRunner {
}
But on running the above script I always get NullPointerException in first line ofloginIntoApp() method.I have already used Pagefactory to initialize webelements but i guess "driver" variable is not getting initialized in Login class though i have inherited asbtract class where i m instantiating the driver due to which it is throwing null pointer.Please see what I am i doing wrong here.Error is as follows:
1 Scenarios ([31m1 failed[0m)
3 Steps ([31m1 failed[0m, [36m1 skipped[0m, [32m1 passed[0m)
0m18.149s
java.lang.NullPointerException
at pages.loginPage.loginIntoApp(loginPage.java:22)
at stepDefination.SmokeTest.I_enter_valid_username_and_password(SmokeTest.java:33)
at ?.When I enter valid "t1#gmail.com" and "pass"(MyApp.feature:5)
Thanks
please add the following constructor. It may solve the issue.
public loginPage(){
PageFactory.initElements(driver, loginPage.class);
}
you can put outside as well like,
public void loginIntoApp() {
String url=abstractPage.driver.getCurrentUrl();
System.out.println("the url is::::::::::::::::"+url);
loginPage lp=PageFactory.initElements(abstractPage.driver, loginPage.class);
lp.username.sendKeys("testuser");
lp.password.sendKeys("123");
}
other way is,
public class SmokeTest extends abstractPage {
#Given("^Open Firefox and start application$")
public void Open_Firefox_and_start_application() throws Throwable {
openBrowsernURLhit();
}
#When("^I enter valid \"([^\"]*)\" and \"([^\"]*)\"$")
public void I_enter_valid_username_and_password(String arg1, String arg2) throws Throwable {
loginPage lpobj = PageFactory.initElements(driver, loginPage.class);
lpobj.loginIntoApp();
}
#Then("^user should be able to login successfully$")
public void user_should_be_able_to_login_successfully() throws Throwable {
loginPage lpobj = PageFactory.initElements(driver, loginPage.class);
lpobj.clicklogn();
}
}
the simplest way to solve the problem is to make your driver object static.
protected static WebDriver driver;
as you are using page factory, you also need to initiazlize the page object, otherwise all the the WebElement will be null
public loginPage(){
PageFactory.initElements(driver, this);
}
or this, which I don't think is a good practice and people should avoid this.
public void loginIntoApp() {
String url=driver.getCurrentUrl();
System.out.println("the url is::::::::::::::::"+url);
PageFactory.initElements(driver, this);
username.sendKeys("testuser");
password.sendKeys("123");
}

Parallel execution using POM with PageFactory

I started encountering problems when I use static objects reference for WebDriver and run the tests in parallel.
public static WebDriver driver;
Hence I decided to use non-static object reference for the WebDriver.
private WebDriver driver;
Now when I use POM with Page Factory, my understanding is that everytime I create a Test I will have to be creating a new Object in the test class as shown below.
/* Constructor in the Page Object class */
private WebDriver driver;
public LoginPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
2 testcases as shown below in the same class.
private LoginPage loginPage;
#Test
public void testCase1() {
loginPage = new LoginPage(getDriver());
loginPage.sendkeys("sometext");
}
#Test
public void testCase2() {
loginPage = new LoginPage(getDriver());
loginPage.sendkeys("sometext");
}
My question here is a
Am I right in creating page object for every test cases?
Is there any way I can optimize this? Because One doubt I got is that non-static object reference may be getting overridden and causing problems in one of the methods if I run them in parallel.
Sorry if my query is naive. Any help would be appreciated.
You do not need to initialize it again. Also, initialize the pages in #BeforeTest rather than in test cases.
Here i would like to give you example of Page object model. Hope you can relate this.
My Main test:
#Before
public void SelectBrowser(){
driver = WebUtils.SelectBrowser(driver,"Chrome");
}
#Test
public void LoginToGmail() throws InterruptedException{
//WebDriver driver = new FirefoxDriver();
//MAximize the Screen
driver.manage().window().maximize();
//Go to Gmail Login Page
SignInPage SignInPage = new SignInPage();
WebUtils.GoToSignInPageForPropertyFile(driver, "URL");
//Click on Next
SignInPage.ClickToLogin(driver, By.cssSelector("input[id='next']"));
Now Supporting class:
GoToSignInPageForPropertyFile method will be in WebUtils
Whatever i write in Webutils will be used by each page object class.
For e.g.
public class WebUtils {
public static pageobject.SignInPage GoToSignInPageForPropertyFile(WebDriver driver, String URL) {
ReadFileData File = new ReadFileData();
Properties Values = File.ReadFile();
driver.get(Values.getProperty("URL"));
return PageFactory.initElements(driver, pageobject.SignInPage.class);
}
}
Now the method ClickToLogin is defined under SignInPage class as:
public class SignInPage {
public EmailViewPage ClickToLogin(WebDriver driver, By by) {
WebUtils.Click(driver, by);
return PageFactory.initElements(driver, EmailViewPage.class);
}
}
Which will further be in Webutils
public class WebUtils {
public static void Click(WebDriver driver, By by) {
WebElement Element = driver.findElement(by);
Element.click();
}
}

Selenium WebDriver, How to run/connect next class

I'm new to Selenium Webdriver, I have a few question :
First, I have 2 class, 'Login.java' and 'SelectCity.java
Class 1 : Login.java
invalidLogin
validLogin
Class 2 : SelectCity.java
For now, in Login.java I wrote
#After
public void tearDown() throws Exception {
driver.close();
}
which mean the browser will closed after finish run right?
Here is my question :
How to make after I run validLogin, it will continue to run the next class (SelectCity.java)?
Is it possible to do that?
How to make last browser (which test valid login) didn't get close?
My current Login.java class :
public class Login extends SelectCityAfterLogin{
private WebDriver driver;
private String baseUrl;
private StringBuffer verificationErrors = new StringBuffer();
#Before
public void setUp() throws Exception {
driver = new FirefoxDriver();
baseUrl = "http://mysite/";
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void testLoginInvalidPass() throws Exception {
driver.get(baseUrl + "/mysite/login.aspx");
driver.findElement(By.id("txtUsername")).sendKeys("user1");
driver.findElement(By.id("txtPassword")).sendKeys("somePassword");
driver.findElement(By.id("lbLogin")).click();
try {
assertEquals("Invalid User or Password", driver.findElement(By.id("lblError")).getText());
} catch (Error e) {
verificationErrors.append(e.toString());
}
}
#Test
public void testLoginInvalidUsername() throws Exception {
driver.get(baseUrl + "/mysite/login.aspx");
driver.findElement(By.id("txtUsername")).sendKeys("username");
driver.findElement(By.id("txtPassword")).sendKeys("somepassword");
driver.findElement(By.id("lbLogin")).click();
try {
assertEquals("Invalid User or Password", driver.findElement(By.id("lblError")).getText());
} catch (Error e) {
verificationErrors.append(e.toString());
}
}
#Test
public void testLoginNoUsername() throws Exception {
driver.get(baseUrl + "/mysite/login.aspx");
driver.findElement(By.id("txtUsername")).sendKeys("");
driver.findElement(By.id("txtPassword")).sendKeys("somePassword");
driver.findElement(By.id("lbLogin")).click();
try {
assertEquals("Please fill username and password.", driver.findElement(By.id("lblError")).getText());
} catch (Error e) {
verificationErrors.append(e.toString());
}
}
#Test
public void testLoginNoPassword() throws Exception {
driver.get(baseUrl + "/mysite/login.aspx");
driver.findElement(By.id("txtUsername")).sendKeys("username");
driver.findElement(By.id("txtPassword")).sendKeys("");
driver.findElement(By.id("lbLogin")).click();
try {
assertEquals("Please fill username and password.", driver.findElement(By.id("lblError")).getText());
} catch (Error e) {
verificationErrors.append(e.toString());
}
}
#Test
public void testLoginValid() throws Exception{
//SelectRoleAfterLogin selectRole = SelectRoleAfterLogin();
driver.get(baseUrl + "/mysite/login.aspx");
driver.findElement(By.id("txtUsername")).sendKeys("myUsername");
driver.findElement(By.id("txtPassword")).sendKeys("password");
driver.findElement(By.id("lbLogin")).click();
Thread.sleep(3000);
}
}
Thx
Yes you can run selectcity after login
You should Extend your First class to Second class
In your Login.java extend it to selectcity java like below
Public class login extends Selectcity {
After your validlogin
create a new instance for selectcity (If you are using #test, create the below in that)
selectcity test = new selectcity()
Once after your valid login do the following
test.(will fetch you the methods available in selectcity.java)
By this way you can call once class to another.
Let me know if this helps
UPDATE :
if selectcityAfterlogin in public class Login extends **SelectCityAfterLogin** is your second class name, then you are creating an incorrect object i guess
In your public login class please check whether your are extending the proper second class name
In #test method
#Test
public void testLoginValid() throws Exception{
//SelectRoleAfterLogin selectRole = SelectRoleAfterLogin();
it should be
SelectCityAfterLogin selectrole = new SelectCityAfterLogin()
selectrole.(will fetch you the methods of second city)
Please let me know if this helps.
One better solution would be to create multiple methods than multiple #test. You can call these to single #test suite.
I am assuming, you have 2 classes 'Login.java' and 'SelectCity.java'
as follows:
Class 1 : Login.java
public void invalidLogin (WebDriver driver){
//some operations
}
public void validLogin (WebDriver driver){
//some operations
}
Class 2 : SelectCity.java
public void selectCity (Webdriver Driver){
//some operations
}
Class 3: LoginAndSelectCity.java
public static void main(String args []){
WebDriver driver = new FirefoxDriver();
Login.validLogin(driver);
SelectCity.selectCity(driver);
}
If all classes should be in same package then only it works else you need to import packages or create new child to do inheritance of the classes.

Capture WebDriver Screenshots When Running Parallel Tests With TestNG

I am currently capturing screenshots on failure and success in TestNG by way of overriding the TestListenerAdapter methods onTestFailure, and onTestSuccess respectively. In order to do this you need to specify which driver you want to take a screenshot of.
My question: Is there a good way to capture screenshots when running tests in parallel on the method level?
In order to run tests in parallel, each individual test needs a unique driver instance. So, at any given time you have x number of driver instances running. When it comes time to capture a screenshot, how do you determine which driver to use?
Code excerpts below:
public class OnFailureListener extends TestListenerAdapter {
#Override
public void onTestFailure(ITestResult tr) {
Screenshots.captureScreenshot(tr);
super.onTestFailure(tr);
}
--
public static void captureScreenshot(ITestResult tr) {
WebDriver driver = TestClass.driver;
if (driver instanceof TakesScreenshot) {
String filename = "path/to/screenshot/file";
try {
File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(scrFile, new File(filename));
} catch (IOException e) { e.printStackTrace(); }
}
If you create a base test class with access to the driver, then that driver will always be the correct driver
The following will achieve this;
All test classes must extend a simple base test class;
public asbtract baseTestCase() {
private WebDriver driver;
public WebDriver getDriver() {
return driver;
}
#BeforeMethod
public void createDriver() {
driver=XXXXDriver();
}
#AfterMethod
public void tearDownDriver() {
if (driver != null){
try{
driver.quit();
}
catch (WebDriverException e) {
System.out.println("***** CAUGHT EXCEPTION IN DRIVER TEARDOWN *****");
System.out.println(e);
}
}
}
In your listener, you need to access the base class:
public class ScreenshotListener extends TestListenerAdapter {
#Override
public void onTestFailure(ITestResult result){
Object currentClass = result.getInstance();
WebDriver webDriver = ((BaseTest) currentClass).getDriver();
if (webDriver != null){
File f = ((TakesScreenshot)webDriver).getScreenshotAs(OutputType.FILE);
//etc.
}
}
}