I have written my even first code using Selenium Web driver, Maven and Java. Can someone give expert opinion if I am in the right direct and if this particular code can be improved.
public class myFirstTest {
#Test
public void startWebDriver(){
WebDriver driver = new FirefoxDriver();
driver.navigate().to("http://www.ft.com/home/uk");
Assert.assertTrue("title should start with World Business", driver.getTitle().equals("World business, finance, and political news from the Financial Times - FT.com"));
driver.findElement(By.id("ftLogin-signIn")).click();
driver.findElement(By.xpath("/html/body/div[4]/div[2]/div[1]/div[2]/div[2]/div[3]/form/fieldset[1]/input")).sendKeys("xxx#mail.com");
driver.findElement(By.id("ftLogin-password")).sendKeys("xxxxxxxx");
driver.findElement(By.xpath("/html/body/div[4]/div[2]/div[1]/div[2]/div[2]/div[3]/form/fieldset[3]/p[1]/button")).submit();
driver.quit();
}
}
Depending is you are learning or creating a test framework for your company there are some pointers.
You functions and values for finding elements are not optimal. cssSelector or id is to prefer.
;
By.cssSelector("input[id$='ftLogin-username']");
or
By.id("ftLogin-username");
Never use xpath if not is absolutly necessary. Escpecially those dependending on the DOM object. Instead change the webpage implemenation to set an id on the webpage elements.
For the button which doesnt have a unique class or id i would use the text instead
driver.findElement(By.cssSelector("button:(*'Sign in'*)"));
If this login part is to be used in several test cases, create a helper class with a login function instead. Otherwise you have to wright the same code over and over again. If a id or xpath where to change you also have to update all your test cases which uses that element.
Add an assertion att the end of the test case to verify that the login has been successful.
Definitely the code you have written can be improved.
Considering the fact, you have just started with WebDriver, below are few suggestions-
Avoid using such long xpaths for locating elements. It has lots of
dependency on DOM. Use short xpaths wherever possible or go for
using other locators.
You can read data from files(s) required for your tests.(example : username, password)
You can use annotations such as #BeforeClass, #BeforeMethod etc., if you are using any unit testing framework to run your tests such as JUnit, TestNG.
Related
I have developed UI page which has href to external link. Now this external link is built (with db based key values for http request parameters) in a class which extends GenericLink of Tapestry. I am overriding public ILink getLink(IRequestCycle cycle) and public String getHref() methods for that. The developed task is working as expected.
Now I want to write JUnit tests for this class, but I read there is other mechanism for Unit testing in Tapestry but this is again not clear to me w.r.t. GenericLinks.
Any leads to any artical will really help in order to test GenericLink class.
I recently moved from Java test automation to Python test automation. In Java, I would use a field annotation named #Important to specify which elements in the Page Object Model are important (so I can query the important elements for each page and check whether they are displayed). This looks similar to this:
class MyPageObjectModel {
#Important
#FindBy(...)
WebElement myElement1;
#Important
#FindBy(...)
WebElement myElement2;
#FindBy(...)
WebElement optionalElement;
}
I wonder whether something similar is available for Python? I found out about decorators, but those seem to be only applicable to functions and not to fields.
Building a selenium framework using POM and trying to add common action class for repeatedly used functions like set text, get text, set radiobutton, listbox etc., structure like below
Test --> POM page object
POM page object --> common action class
#Test
public void verifyGooglebuttontext() {
System.out.println("set search value");
googlepage.setSearchValue("Google Test");
Assert.assertEquals(addC.verifytext(),"Google");
}
POM
#FindBy(name="q")
WebElement txtGoogleSearch;
public static setSearchValue(String txt) {
setText(txtGoogleSearch, txt)
}
Common Action class
public void setText(WebElement element,String Value ){
element.sendKeys(Value);
}
My query is whether this will impact in performance as I have to call 2 functions rather than setting and getting control values directly in Page object class.
Please advise.
Given that most of the time you'll wait for the page loading and search for elements in the loaded page's DOM, I wouldn't worry too much about two extra function calls.
Really, page loading will take ages (> milliseconds) in comparison with a method call (microseconds or less).
What you gain from that is massively better: if the UI changes, you'll have to revisit a small number of places to adjust to the new element locations/type/ways to carry an action (while your #Test logic can stay virtually unchanged). Which means you'll be up-and-running in hours rather than a couple of day to go all over your code and locate/change all places affected by a change in UI.
In this case, you aren't reducing redundancy, you are adding to it. You are taking a one liner driver.findElement(...).sendKeys(value) and changing it to a different one liner setText(driver.findElement(...), value) but creating an additional function setText(). There's no need to add another layer of complexity here. It doesn't save any time here or during maintenance.
#FindBy(name="q")
WebElement txtGoogleSearch;
public static setSearchValue(String txt) {
txtGoogleSearch.sendKeys(txt);
}
I would not use PageFactory. If you test sites of any complexity, you will find yourself refetching elements on the page after the initial load. To avoid this, define your locators are part of the page object and use them to fetch elements when you need them.
Sample page object
public class AutomationPracticeTablePage
{
private WebDriver driver;
private By pageTitleLocator = By.xpath("//h1[text()='Automation Practice Table' and #class='h1-size']");
private By tableHeadingLocator = By.cssSelector("table > thead th");
public AutomationPracticeTablePage(WebDriver webDriver) throws IllegalStateException
{
this.driver = webDriver;
// wait for page to finish loading
new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(pageTitleLocator));
// see if we're on the right page
if (!driver.getCurrentUrl().contains("/automation-practice-table/"))
{
throw new IllegalStateException("This is not the Automation Practice Table page. Current URL: " + driver.getCurrentUrl());
}
}
public List<String> getHeadings()
{
List<String> headings = new ArrayList<String>();
for (WebElement heading : driver.findElements(tableHeadingLocator))
{
headings.add(heading.getText());
}
return headings;
}
}
Sample code to use the page object
driver.get("http://toolsqa.com/automation-practice-table/");
AutomationPracticeTablePage automationPracticeTablePage = new AutomationPracticeTablePage(driver);
System.out.println(automationPracticeTablePage.getHeadings());
I would advise you start by not abbreviating Page Object Model as POM. POM is associated to Maven as a project dependency management file. Also to get your your questions in a more clear state. The more clear and concise you are, the better and quicker support you will get. You can visit http://toolsqa.com/ to get better understanding of Page Object Model structure, get creative, and make better your terminology.
This is a #Test script (method), not a class. It is also not a page object model. Any method annotated with this is considered a Test script to be executed.
#Test
public void verifyGooglebuttontext() {
System.out.println("set search value");
googlepage.setSearchValue("Google Test");
Assert.assertEquals(addC.verifytext(),"Google");
}
This is a WebElement and a functional method within a page object. Not a Page Object Model
#FindBy(name="q")
WebElement txtGoogleSearch;
public static setSearchValue(String txt) {
setText(txtGoogleSearch, txt)
}
This is not a class. It is a method in a class.
public void setText(WebElement element,String Value ){
element.sendKeys(Value);
}
Okay, now, your setText(WebElement, String) method is too thin to provide any value as is. You can however, add additional support into the method if you find it necessary. Most of my experience doesn't require additional support for input fields, but I have ran across some that do need additional support when javascript is involved; DOM manipulation after page load.
The point and advise here, is that don't over complicate what isn't necessary. Work with what currently works, and tweak for support as you go. Now, if you can see further into the outlook of the project that you will be needing such support, go for it, and implement the support ahead of time.
I am using Selenium Webdriver with QAF.
The issue I am facing is related to finding an element on webpage.
for few of elements, different locators work at different times.
For example - sometimes name=nameA works and sometimes name=nameB(may be depending upon different environments of AUT, I have no clue).
Find code below:
public class HomePage extends WebDriverBaseTestPage<WebDriverTestPage> {
#FindBy(locator="name=nameA")
private QAFWebElement btnSomeElement;
#Override
protected void openPage(PageLocator locator, Object... args) {
driver.get("/");
}
}
What should I do to come over this issue?
While you are already using QAF you already have solutions available for such use case. First of all you should use Locator repository,
Instead of hard-coding locator in page just provide locator key.
For example:
In page.loc File
my.ele.locator=<locatorStretegy>=<locator>
my.ele.locator=name=elemName
In Page class:
#FindBy(locator = "my.ele.loc")
private QAFWebElement btnSomeElement; Now coming to your problem, if most of the locator very with environment then you can utilize
resource management capabilities of QAF. In other case you can
use alternate locator strategy provided by QAF. For example:
my.ele.locator=['css=.cls#eleid','name=eleName','name=eleName2']
my.ele.locator=['name=eleNameEnv1','name=eleNameEnv2']
You should get a selector that matches both environments, use css or xpath selectors.
Add html snippet with the sections/elements if you need any help.
Assuming that you are selecting by name and this name changes, you can write a selector that matches both names like:
css: [name=nameA], [name=nameB]
xpath: //*[#name='nameA' or #name='nameB']
It is very common that the locator changes sometimes. Some are even dynamic especially elements in the table rows. It is better to use more specific locator like xpath to locate the element.
#FindBy(locator = "xpath=//button[text()='ButtonName']")
private QAFWebElement btnSomeElement;
btn.login.page.logon.button = {\"locator\":[\"xpath=//div[contains(#id,'logonbutton')]//span[contains(text(),'Logon')]\",\"xpath=//*[contains(text(),'Logon') or #id='__screen0-logonBtn']\"]}
This is an example of the same for plain text file using multiple locators in QAF. Works for me.
I am trying to encode input form data here. There are two options and I have tried both of them:
Use URLencoder.encode(inputString) method which does not work on GWT client side (My code resides in client module) Results in error 'Did you forget to inherit required module?'
URL.encodeQueryString(inputString) which works well, But when I run relevant test cases using JUnit, all I get is unsatisfiedlinkederror
Are there any alternatives for encoding method or is there any work around for above mentioned methods?
For your second option :
GWT uses modules and needs to be compiled, which is different than running a simple JUnit test. Take a look at http://www.gwtproject.org/doc/latest/DevGuideTesting.html, they explain how to setup JUnit test.
Just use the URL class and its methods:
URL.encode(String decodedURL)
URL.encodeQueryString(String decodedURLComponent)
Do not forget to inherit the required module <inherits name="com.google.gwt.http.HTTP"/>.
For URL building, I use the "UrlBuilder": com.google.gwt.http.client.UrlBuilder
UrlBuilder u = new UrlBuilder();
u.setProtocol("https");
u.setHost("www.mysite.com:8080");
u.setPath("/myServletPath");
u.setParameter("username", nameField.getValue());
u.setParameter("someAttribute", "itsValue");
u.buildString();
This code will result in:
https://www.mysite.com:8080/myServlet?username=GWT%20User&someAttribute=itsValue