I'm using POM framework and managed the code in following way :
This is My InviteFriendPage
public class InviteFriends
{
#FindBy(xpath ="//li/a[normalize-space()='Invite Friends']")
public WebElement inviteFriendsLink;
#FindBy(id = "divLoader")
public WebElement loader;
#FindBy(xpath = "//input[#name='lstInviteFriendsModel[1].FriendName']")
public List<WebElement> friendName;
#FindBy(xpath = "//input[#name='lstInviteFriendsModel[1].FriendMobile']")
public WebElement friendNumber;
WebDriver driver;
public InviteFriends(WebDriver driver)
{
PageFactory.initElements(driver, this);
this.driver=driver;
}
public void inviteFriend(String friendName, String friendMobile)
{
JavascriptExecutor js = (JavascriptExecutor)driver;
js.executeScript("arguments[0].click();", inviteFriendsLink);
for(int i=1;i<=3;i++)
{
driver.findElement(By.xpath("//input[#name='lstInviteFriendsModel["+i+"].FriendName']")).sendKeys(friendName);
driver.findElement(By.xpath("//input[#name='lstInviteFriendsModel["+i+"].FriendMobile']")).sendKeys(friendMobile);
}
}
}
This is my Executor Class from where I'm calling all the pages by creating object
public class MainSuitExecuter extends DriverSetup
{
#Test()
public void submitFeedback() throws InterruptedException
{
ContactUsPage conpage = new ContactUsPage(driver);
conpage.submitContactUsForm(TestDataComman.fName, TestDataComman.fEmail, TestDataComman.fMobile, TestDataComman.fType, TestDataComman.fMessage);
}
#Test()
public void login() throws IOException, InterruptedException
{
UserLogin login = new UserLogin(driver);
ReadExcel read = new ReadExcel();
String uname = read.getCellData(1, 0);
String passwd = read.getCellData(1, 1);
login.doLogin(uname, passwd);
}
#Test()
public void inviteFriend()
{
InviteFriends invitefriend = new InviteFriends(driver);
invitefriend.inviteFriend();
}
}
and Executing the MainSuitExecuter class from testing.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="MyKart">
<test name="Functional_Test">
<parameter name="browser" value="chrome" />
<classes>
<class name="com.commonutils.DriverSetup"/>
<class name="com.testexecuter.MainSuitExecuter"/>
</classes>
</test>
</suite>
The Problem is, For friend Invitation minimum 3 friends details(friendname and number) mandatory. So i'm using DataProvider of TestNG for that. But not getting the clear idea where i need to use in My code as i have structure as mentioned above.
I have tried it in InviteFriend.java class Like :
#Test(getData)
public void inviteFriend(String friendName, String friendMobile)
{
JavascriptExecutor js = (JavascriptExecutor)driver;
js.executeScript("arguments[0].click();", inviteFriendsLink);
for(int i=1;i<=3;i++)
{
driver.findElement(By.xpath("//input[#name='lstInviteFriendsModel["+i+"].FriendName']")).sendKeys(friendName);
driver.findElement(By.xpath("//input[#name='lstInviteFriendsModel["+i+"].FriendMobile']")).sendKeys(friendMobile);
}
}
#DataProvider
public Object[][] getData()
{
Object[][] data = new Object[3][2];
// 1st row
data[0][0] ="A";
data[0][1] = "9442307801";
// 2nd row
data[1][0] ="B";
data[1][1] = "9887252210";
// 3rd row
data[2][0] ="C";
data[2][1] = "9925497562";
return data;
}
but no success as i have to call inviteFriend() method from MainSuitExecuter class so there i need to pass parameter.
Can anybody help me to get out from this glitch and suggest me better idea to accomplish my motive
This how you can probably do.
Create a separate Friend Class. with attribute you required, i.e. Name and mobile number
Create the object or list of object of this friends. I think list of object would be better.
Rather than using data provider, in your test create this friend object and pass it some of the method in inviteFriend class.
I think this would be cleaner approach and better to understand.
Sample code
Friend.java
public class Friend {
String name;
String mobile;
public Friend(String name, String mobile){
this.name = name;
this.mobile = mobile;
}
}
InviteFriends.java
public class InviteFriends {
public InviteFriends(WebDriver driver){
PageFactory.initElements(driver, this);
}
public void createFriendInvitation(List<Friend> friendList){
for (Friend friend: friendList) {
System.out.println(friend.mobile);
System.out.println(friend.name);
}
}
}
Your Test Class
public class TestClass {
#Test
public void testFriendInvitation(){
WebDriver driver = new FirefoxDriver();
List<Friend> friends = new ArrayList<Friend>();
friends.add(new Friend("bestfriend", "11111"));
friends.add(new Friend("newfriend", "222"));
friends.add(new Friend("oldfriend", "33333"));
InviteFriends inviteFriends = PageFactory.initElements(driver, InviteFriends.class);
inviteFriends.createFriendInvitation(friends);
driver.quit();
}
}
The code is pretty much good except one. We need a give a name to the data provider annotation and refer it as a source of input for the respectective #test method. The code below will ideally work for you,
public class MainSuitExecuter extends DriverSetup
{
#Test()
public void submitFeedback() throws InterruptedException
{
ContactUsPage conpage = new ContactUsPage(driver);
conpage.submitContactUsForm(TestDataComman.fName, TestDataComman.fEmail, TestDataComman.fMobile, TestDataComman.fType, TestDataComman.fMessage);
}
#Test()
public void login() throws IOException, InterruptedException
{
UserLogin login = new UserLogin(driver);
ReadExcel read = new ReadExcel();
String uname = read.getCellData(1, 0);
String passwd = read.getCellData(1, 1);
login.doLogin(uname, passwd);
}
#Test(dataProvider="users") //use dataprovider with name "users" as input source
public void inviteFriend(String name, String number)
{
InviteFriends invitefriend = new InviteFriends(driver);
invitefriend.inviteFriend(name, number);
}
#DataProvider(name = "users") //naming as users
public Object[][] getData()
{
Object[][] data = new Object[3][2];
// 1st row
data[0][0] ="A";
data[0][1] = "9442307801";
// 2nd row
data[1][0] ="B";
data[1][1] = "9887252210";
// 3rd row
data[2][0] ="C";
data[2][1] = "9925497562";
return data;
}
}
DataProvider becomes handy when we need to pass the data from excel sheet becomes handy. More details are available in the testng documentation.
http://testng.org/doc/documentation-main.html#parameters-dataproviders
Some tutorials for data provider implementation
http://learn-automation.com/data-driven-framework-in-selenium-webdriver/
I hope this helps you.
Thanks.
Related
I use Selenium.
I have Page Object like this:
public class Portal extends Utils{
private WebDriver driver;
private final By getReason= By.xpath("//a[contains(text(),'Get Sol')]");
public Portal(WebDriver driver) {
super(driver);
this.driver = driver;
}
public VisitReason clickCurrentReason() {
clickIn(getReason);
return new VisitReason(driver);
}
}
I would like this method: clickCurrentReason() return new Object extend Utils class and passed it parameter: driver.
How to do it?
I know I have to use generics. I found part of solution:
public<T extends Utils> T clickCurrentReason(){
clickIn(getReason);
return (T)(driver);
}
But how passed in return: "return new Object(driver)"
#Test method:
public void Test() {
TestingEnv testingEnv = new TestingEnv(driver);
Portal portal = testingEnv.openPage();
VisitReason visitReason = portal.clickCurrentReason();
//sometimes instead of the last line it will be: VisitInSpot visitInSpot = portal.clickCurrentReason();
//sometimes instead of the last line it will be: VisitBack visitBack = portal.clickCurrentReason();
}
Can we follow the below method to initialise the test data? There are 2 points I want to implement.
Need to initialise/load the test data once from the file and use the same test data in all dataproviders.Thought to implement test data loader in #beforesuite class.
Need data from dataprovider and a parameter from testNG file at the same time in #test method.
#BeforeSuite
#Parameters(value = { "test_data_file" })
public static synchronized void init(String test_data_file) {
TestDataFactory.load(test_data_file);
}
#Test(dataProvider="dp_dummy",dataProviderClass = DP_1.class)
public void testDummyAPI(TestData test_data,ITestContext context){
String param = context.getCurrentXmlTest().getParameter("param");
}
#DataProvider(name = "dp_dummy")
public Object[][] getDataFromDataprovider(ITestContext context) {
List<TestData> test_data_collection = TestDataFactory.getTestData(targated_test_data);
Object[][] test_data_set = new Object[test_data_collection.size()][1];
for(TestData test_data : test_data_collection)
test_data_set[i++][0] = test_data;
return test_data_set;}
Assuming you are creating your test_data_set correctly you can achieve your second point like this
#Test(dataProvider="dp_dummy",dataProviderClass = DP_1.class)
public void testDummyAPI( String p, Object[][] ob){
System.out.println(p);
System.out.println(ob[0][0]);
}
#DataProvider(name = "dp_dummy")
public Object[][] getDataFromDataprovider(ITestContext context) {
List<TestData> test_data_collection = TestDataFactory.getTestData(targated_test_data);
Object[][] test_data_set = new Object[test_data_collection.size()][1];
for(TestData test_data : test_data_collection)
test_data_set[i++][0] = test_data;
String param = context.getCurrentXmlTest().getParameter("param");
return new Object[][] {
{ param, test_data_set}
};
}
Im currently using a Data Provider to enter numerous different users, but when the tests ends then the previous user is still logged in, how do make the bowser reset after each run? Heres my code:
public class login_from_login_page extends ConditionsWebDriverFactory {
public static final int TEST_CASE_PASSED_STATUS = 1;
public static final int TEST_CASE_FAILED_STATUS = 5;
#Test(dataProviderClass = Data.DataProviders.class, dataProvider = "customer")
public void LoginActionTest (String pass, String email, String user) throws Exception{
Header header = new Header();
header.guest_select_login();
Pages.Login login = new Pages.Login();
login.LoginAction(pass, email, user);
//TestRail.addResultForTestCase("16870",TEST_CASE_PASSED_STATUS," ");
//TestRail.addResultForTestCase("16874",TEST_CASE_PASSED_STATUS," ");
//TestRail.addResultForTestCase("17199",TEST_CASE_PASSED_STATUS," ");
}
}
I have a Webdriverfactory class that is extended from my test class here but its doesn't seem to be creating a new instance, im always still logged in when it restarts using the new DataProvider information.
#Listeners({ScreenShotOnFailListener.class})
public class ConditionsWebDriverFactory {
#BeforeClass
public void beforeTest() {
Drivers.startBrowser(true);
Drivers.getDriver().manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
}
#AfterClass (alwaysRun = true)
public void afterTest() {
Drivers.finishBrowser();
}
}
Start your test with initialization before test execution:
#BeforeMethod
void setupTest() {
WebDriver driver = new ChromDriver();
// method code here
}
Alternatively initialize your page object with new driver instance:
Pages.Login login = new Pages.Login(new ChromDriver());
Another alternative:
#Test
void seleniumTest() {
// Test code here
login.getDriver().manage().deleteAllCookies();
login.getDriver().navigate.refresh();
}
You can add after method, if you use testng:
#AfterMethod
void deleteCoockies() {
Drivers.getDriver().manage().deleteAllCookies();
Drivers.getDriver().get ("homePageUrl");
}
package com.objects;
import java.util.ArrayList;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;
import org.openqa.selenium.support.PageFactory;
import com.tests.BaseClass;
public class LinkedInHomePage extends BaseClass {
public LinkedInHomePage(WebDriver driver) {
PageFactory.initElements(driver, this);
}
#FindBy(how = How.ID, using = "firstName-coldRegistrationForm")
public WebElement newFirstNameTexthBox;
#FindBy(how = How.NAME, using = "lastName")
public WebElement newLastNameTexthBox;
#FindBy(how = How.ID, using = "email-coldRegistrationForm")
public WebElement newEmailTexthBox;
#FindBy(how = How.ID, using = "password-coldRegistrationForm")
public WebElement newPasswordTexthBox;
#FindBy(how = How.ID, using = "btn-submit")
public WebElement signUpButton;
public void EnterNewFirstName(String inComingNewFirstName) {
newFirstNameTexthBox.clear();
newFirstNameTexthBox.sendKeys(inComingNewFirstName);
}
public void EnterNewLastName(String inComingNewLastName) {
newLastNameTexthBox.clear();
newLastNameTexthBox.sendKeys(inComingNewLastName);
}
public void EnterNewEmail(String inComingNewEmail) {
newEmailTexthBox.clear();
newEmailTexthBox.sendKeys(inComingNewEmail);
}
public void EnterNewPassword(String inComingNewPassword) {
newPasswordTexthBox.clear();
newPasswordTexthBox.sendKeys(inComingNewPassword);
}
public void ClickSignUp() {
signUpButton.click();
}
public void JoinNow(String FName, String LName, String Email,
String Password) {
EnterNewFirstName(FName);
EnterNewLastName(LName);
EnterNewEmail(Email);
EnterNewPassword(Password);
ClickSignUp();
}
}
The JoinNow() function above as you can see takes multiple parameters and it works perfectly fine. I would like use array or list to reduce
the number of arguments and then use loop to fill the text boxes. I want to accomplish something similar as below, but since I am using Page Object Model Design, can't use findElement.
#SuppressWarnings("unchecked")
protected void JoinNow(String... var) {
List<MyElements> inputElements = new ArrayList<MyElements>();
inputElements.add((MyElements) driver.findElement(By
.id("firstName-coldRegistrationForm")));
inputElements.add((MyElements) driver.findElement(By.id("lastName")));
inputElements.add((MyElements) driver.findElement(By
.id("email-coldRegistrationForm")));
inputElements.add((MyElements) driver.findElement(By
.id("password-coldRegistrationForm")));
for (int i = 0; i < var.length; i++) {
((WebElement) inputElements.get(i)).sendKeys(var[i]);
}
}
Why not send a Map to JoinNow method. I suggest you keep your PageObject pattern as is. I believe this is more readable and more maintainable long term
Map<String,String> data = new HashMap<String,String>();
data.put("firstname", "George");
data.put("lastname", "Clooney");
data.put("email", "George#xyz.com");
data.put("password", "Dontguess");
JoinNow(data);
protected void JoinNow(Map<String,String> data) {
firstNameElement.sendKeys(data.get("firstname"));
lastNameElement.sendKeys(data.get("lastname"));
emailElement.sendKeys(data.get("email"));
passwordElement.sendKeys(data.get("password"));
}
There are couple of items you need to consider while doing page object model. Based on how you have done it (by using #FindBy), sometimes you will end up with stale data. For better results you should be defining your ids like below and use them to get you Webelemment. This is how i would design it. Define all your By.id and put it into a list and use the function below to simplify it.
Definition of by:
static final By FNAME_REGFORM_BY_ID = By.id("firstName-coldRegistrationForm");
public void fillTextBoxes(List<By> bys, List<String> valuesToPopulate) {
List<WebElement> webelements = new ChromeDriver().findElements(By.cssSelector("input[type=text]"));
for (int i = 0; i < bys.size(); i++) {
new ChromeDriver().findElement(bys.get(i)).sendKeys(valuesToPopulate.get(i));
}
}
Edit: For better understanding, fillTextBoxes was equivalent of joinNow.
List<String> values=new LiskedList<String>();
Values.add("emailid");
values.add("firstname");
List<By> bys=new LinkedList<By>();
bys.add(FNAME_REGFORM_BY_ID);
bys.add(EMAIL_ID_By_ID);
public void joinNow(List<By> bys, List<String> values) {
List<WebElement> webelements = new ChromeDriver().findElements(By.cssSelector("input[type=text]"));
for (int i = 0; i < bys.size(); i++) {
new ChromeDriver().findElement(bys.get(i)).sendKeys(valuesToPopulate.get(i));
}
}
Don't use Annotated #FindBy(how = How.ID, using = "email-coldRegistrationForm") etc, as this slow down execution as well as gives you stale data. FindBy tends to slow down as the entire page object becomes completely initialized only after all elements are available. Try using Static By like i mentioned. We ran into perf issues and started switching from annotated Findby.
Based on request, here is a sample implementation. You might have to tweak based on your project requirements, but this should get started.
Your Page Class (renamed as SO for my sake).
public class SO {
WebDriver driver;
static final By FNAME_REGFORM_BY_ID = By.id("firstName-coldRegistrationForm");
static final By LNAME_REGFORM_BY_NAME = By.className("lastName");
static final By EMAIL_REGFORM_BY_ID = By.id("email-coldRegistrationForm");
static final By PWD_REGFORM_BY_ID = By.id("password-coldRegistrationForm");
static final By SUBMIT_REGFORM_BY_ID = By.id("btn-submit");
public SO(WebDriver driver) {
this.driver = driver;
}
public List<By> getAllInput() {
List<By> bys = new LinkedList<By>();
bys.add(FNAME_REGFORM_BY_ID);
bys.add(LNAME_REGFORM_BY_NAME);
bys.add(LNAME_REGFORM_BY_NAME);
bys.add(LNAME_REGFORM_BY_NAME);
bys.add(LNAME_REGFORM_BY_NAME);
return bys;
}
public void joinNow(List<String> values) {
PageFactory.initElements(this.driver, HotelDetailsPage.class);
List<By> bys = this.getAllInput();
List<WebElement> webElements = this.driver.findElements(By.cssSelector("input[type=text]"));
for (int i = 0; i < bys.size(); i++) {
new ChromeDriver().findElement(bys.get(i)).sendKeys(values.get(i));
}
}
public SO open(final String url) {
this.driver.get(url);
return this;
}
}
Test Method:
#Test
public void testForm() {
WebDriver driver = new FirefoxDriver();
SO so = new SO(driver);
SO soPage = so.open("your app url");
List<String> values = new LinkedList<String>();
values.add("firstname");
values.add("lastName");
values.add("emailId");
values.add("password");
values.add("id");
soPage.joinNow(values);
}
Following is the another way:
HashMap<WebElement, String> d = new HashMap<WebElement, String>();
d.put(newFirstNameTexthBox, "Michael");
d.put(newLastNameTexthBox, "Johnson");
d.put(newEmailTexthBox, "a#b.com");
d.put(newPasswordTexthBox, "Michael123$");
joinNow(d);
protected void JoinNow(Map<WebElement,String> data) {
for (Entry<WebElement, String> objValue : data.entrySet())
objValue.getKey().sendKeys(objValue.getValue());
}
I created 4 classes also after I decided to convert my project into this design pattern. I moved my codes inside the related classes into the methods. While compiling I'm facing failures and I don't know why.
The main class
GidiyorTest.java
public class GidiyorTest {
protected WebDriver driver;
protected String baseUrl;
private boolean acceptNextAlert = true;
private StringBuffer verificationErrors = new StringBuffer();
static GidiyorTest gittiGidiyor = new GidiyorTest();
static String generatedMail = gittiGidiyor.generateString();
static String generatedUsername = gittiGidiyor.generateString();
static RegisterPage registerPage = new RegisterPage();
static LoginPage loginPage = new LoginPage();
static SearchPage searchPage = new SearchPage();
static DiscountsPage discountsPage = new DiscountsPage();
public String generateString(){
char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 7; i++) {
char c = chars[random.nextInt(chars.length)];
sb.append(c);
}
String output = sb.toString();
return output;
}
#Before
public void setUp() throws Exception {
driver = new FirefoxDriver();
baseUrl = "https://www.gittigidiyor.com/";
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void testGidiyor() throws Exception {
registerPage.Register();
loginPage.Login();
searchPage.Search();
discountsPage.Discount();
}
#After
....
RegisterPage.java (One of the four new classes for instance sharing just one)
public class RegisterPage extends GidiyorTest {
public void Register() throws InterruptedException {
driver.get(baseUrl + "/kayit-ol");
driver.findElement(By.name("name")).clear();
driver.findElement(By.name("name")).sendKeys("murat");
driver.findElement(By.name("surname")).clear();
driver.findElement(By.name("surname")).sendKeys("yilmaz");
Thread.sleep(300);
driver.findElement(By.id("suggestion_email_input_verifier")).clear();
driver.findElement(By.id("suggestion_email_input_verifier")).sendKeys(
generatedMail + "#gmail.com");
driver.findElement(By.id("nickname")).clear();
driver.findElement(By.id("nickname")).sendKeys(generatedUsername);
Thread.sleep(300);
driver.findElement(By.name("passwd")).clear();
driver.findElement(By.name("passwd")).sendKeys("123456abc");
driver.findElement(By.name("passwd2")).clear();
driver.findElement(By.name("passwd2")).sendKeys("123456abc");
Thread.sleep(300);
driver.findElement(By.id("cepgsm")).clear();
driver.findElement(By.id("cepgsm")).sendKeys("531");
driver.findElement(By.id("cep")).clear();
driver.findElement(By.id("cep")).sendKeys("600 29 79");
Thread.sleep(1000);
driver.findElement(By.id("SubmitForm")).click();
}
}
And the error is beginning at registerPage.Register(); line. One another is java.lang.NullPointerException.
Hope you can help.
The way you're creating your PageObject is not correct. You should not extend the test, one of the main points is that the PageObject should not know anything about the test, rather just expose the services offered by the page.
On the other hand your test should hold the assertions and other test related logic.
The second wrong thing is that you should use PageFactory to instantiate your page object, so that you can take advantage of the lazy binding mechanism. So change to something like this
public class RegisterPage {
private WebDriver driver;
public RegisterPage(WebDriver driver) {
this.driver = driver;
}
// The rest of your class
}
and instantiate inside the test using PageFactory
PageFactory.initElements(driver, RegisterPage.class);
also to ease up maintainence and benefit from lazy element binding you can think about adding your elements as fields, and mark them via annotation, so they get populated by PageFactory as well e.g.
public class RegisterPage {
private WebDriver driver;
public RegisterPage(WebDriver driver) {
this.driver = driver;
}
#FindBy(name = "name")
private WebElement name;
...
}
}