Using Java, I am trying to write a general check for a particular text on every page in a web application that existing tests visit. Instead of having to write it on each and every page individually, is it possible to do in one place at a high level (may be in the base class)?
public class BaseClassForUiTest {
public BaseClassForUiTest() {
...
}
public void test() throws Exception {
boolean isNewPage = checkIfNewPage();
if (isNewPage)
// perform a text check on the page
}
}
Every test extends from BaseClassForUiTest and overrides the test() method.
Instead of having to write it on each and every page individually, is it possible to do in one place at a high level (may be in the base class)?
Yes, it is possible by implementing WebDriverEventListener into BaseClassForUiTest and override event handler methods to handling the appropriate WebDriver events according to need in one place.
Here every method corresponds to an event. According to your requirement you need to handle afterNavigateTo() method. This one is called every time the navigate to a page is completed.
You have to do perform a text checker on the page code in this method so that your code is executed every time the page navigates to some other page.
public class BaseClassForUiTest implements WebDriverEventListener
{
---------
---------
public void afterNavigateTo(String arg0, WebDriver arg1) {
// perform desire text checker stuff on the page here
}
}
Now Create Event Throwing WebDriver to perform your test :-
Create a regular WebDriver.
FirefoxDriver driver = new FirefoxDriver();
Now create an EventThrowingWebDriver using our regular WebDriver created above.
EventFiringWebDriver eventDriver = new EventFiringWebDriver(driver);
Create an instance of your eventHandler class and register it for events using the register method of EventFiringWebDriver object created above as :-
BaseClassForUiTest handler = new BaseClassForUiTest();
eventDriver.register(handler);
Full code :-
import org.openqa.selenium.support.events.EventFiringWebDriver;
public class BaseClassForUiTest {
public void test() throws Exception {
FirefoxDriver driver = new FirefoxDriver();
EventFiringWebDriver eventDriver = new EventFiringWebDriver(driver);
BaseClassForUiTest handler = new BaseClassForUiTest();
eventDriver.register(handler);
eventDriver.get("your url");
//Now do your further stuff
}
}
As for me, you'd better create a separated test class for this check using Parameterized or JUnitParams and give it a urls to run where as parameters, but it depends on what is your common approach to running tests (we run all testpack at the same time, so it's a solution for us in this situation).
Also it is seems like well-logically-separated solution
If you are going to use this check as an assertion you can rewrite your current code for this case and call for it in #Before block (but it is still not a good solution, in my opinion)
Related
I am wring a PageFactory framework for a website using maven+TestNG,
I have page wise PageObject classes where all web elements and actions specific to page are present like LoginPageObject, AccountSelectionPageObject...
I have a class "Base" where the common elements like WebDriver, Logger are present.
I have a class "BasePage" where the common actions like click, scroll, select, refresh... are present
MyTestng.xml is having separate <class> entry for both all individual pages.
It's just that I am initializing the browser object in #BeforeSuiit and stored/placed it in the Base class which is being extended in my Test classes
Below is the flow/arch I came up for my project.
Issue:
I have multiple #Test in each of my test classes.
When my Test classes are executed individually, all #Test script executed,
but when I execute them continuously, i.e. my testng file have separate entries for all my test classes, my execution fails. Error says unable to find element on page, I have wait statements, but still it's not working.
I have tried debugging code, but not able to find the reason as the flow stops on starting of second page with exception saying element not found
Code:
#FindBy(id="listAccounts")
WebElement accountDropdown;
public void selectAccount(){
logger.info("Selecting Account");
implicitwait(10);
Select dropdown = new Select(accountDropdown);
logger.info("Drop down is multiple::"+ dropdown.isMultiple());
}
Expected:
Code should execute completely even when separated code page wise.
Actual:
When I have all pages code in one test class, code executed.
But when I place them separately in page wise test class, element not found exception is thrown.
org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"id","selector":"listAccounts"}
In POM using PageFactory framework, one should initialise PageFactory in Constructor of PageClasses. Please find below code snippet which might work in your case.
public class LoginPage extends TestBase {
public LoginPage() {
PageFactory.initElements(driver, this);//Here driver is initialised in TestBase class and inherited in LoginPage class
}
//your code
#FindBy(id="listAccounts")
WebElement accountDropdown;
public void selectAccount(){
logger.info("Selecting Account");
implicitwait(10);
Select dropdown = new Select(accountDropdown);
logger.info("Drop down is multiple::"+ dropdown.isMultiple());
}
}
A complement to the Krishna answer:
PageFactory.initElements(driver, this);
The code above can be moved to the base class and from the LoginPage, you just pass the webdriver on the constructor like this.
public class LoginPage extends Base {
public LoginPage(Webdriver driver) {
super(driver);
}
...
public class Base {
public Base(Webdriver driver) {
PageFactory.initElements(driver, this);
}
...
I am using TestNG in order to run automated mobile tests in parallel using Appium.
I have a method in one class that initiates an AppiumDriver object.
I call this method from my TestNG class in order for each thread to create it's own instance of the driver. Trouble is, because it's a static variable, when each of the threads tries to access the driver from another class via a static method:
There is always conflicts, as each thread only has access to a single implementation of the AppiumDriver object.
I understand i can bypass this by having all this code within a single class that the TestNG XML file communicates with, but this is messy and i'd prefer to have separate classes for different parts of functionality.
For example, i have a custom TestNG listener that i would like to print driver details each thread is using after each test method is run. With the following method:
However, this always prints the last running thread's driver.
How might i go about ensuring the returned driver the from the method is consistent to the driver that was created in the method?
If you are running your test in parallel, then we need to use the non static method to avoid the conflicts. All the above methods needs to be changed as non static.
You need to modify your listener class as below and it will give the driver details correctly. We need to get the current class instance from the ITestResult
Listener Code:
public class ResultsListener implements ISuiteListener, IInvokedMethodListener {
#Override
public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
Object currentClass=testResult.getInstance();
WebDriver driver = ((AppiumSetUp) currentClass).getDriver();
System.out.println("Driver details: " + driver);
}
}
Edit:
You can extends the AppiumSetUp class in all required class(atleast in #BeforeTest method class).So, that you can directly access the setup method as below. For the example purpose, I have added the class name as BeforeTestSetup
public class BeforeTestSetup extends AppiumSetUp{
#BeforeTest(alwaysRun = true)
#Parameters({"platform", "udid", "chromeDriverPort", "chromeDriverPath", "deviceName"})
public void setUp(String platform, String udid, String chromeDriverPort, #Optional String chromeDriverPath, String deviceName) throws Exception {
driver = setUp(platform, udid, chromeDriverPort, deviceName);
}
}
I'm using WebDriver (Selenium) and I want to add custom methods to WebDriver such as driver.performCustomAction().
Being that I could instantiate an instance of FirefoxDriver or ChromeDriver I cannot simply extend FirefoxDriver bec I would not be able to use the functionality with Chrome Driver.
Tech I could create a new class and pass an instance of WebDriver to the constructor (so it could be either FF or Chrome) but then I would be unable to perform all of the non custom actions of each class such as findElements(), getText() on the new object.
In other words, if my new class is called WrappedWebDriver and I instaniate a new instance of it as follows:
WebDriver FFDriver = new FirefoxDriver();
WrappedWebDriver WDriver = new WrappedWebDriver(FFDriver);
I will be able to call WDriver.performCustomAction() but I will not be able to call WDriver.findElement() or any of the other methods defined in the FirefoxDriver class (or the actions I could perform using FFDriver ).
How can I add new methods that apply to both FirefoxDriver and ChromeDriver without writing it twice while retaining all functionality of each respective class?
P.S: I know Java doesn't allow multiple inheritance is there some other way around it?
I think you can create Wrapper class which will hold instance of the Webdriver and you will wrap methods of webdriver which u want to support. You can do it for instance like this:
public class WrappedWebDriver {
public WebDriver driver;
public WrappedWebDriver(WebDriver driver){
this.driver = driver
}
public WebElement find(By by){
//your customization code
return driver.findElement(by);
}
public void setText(By by, String text){
//your customization code
driver.findElement(by).sendKeys(text)
}
public void performCustomAction(){
//your customization code
}
}
You can customize Webdriver standard methods by adding some functionality in wrapped methods. By making driver public, you give user an option to choose beetween using standard driver methods or your customized methods.
There is nice and useful Wrapper API for selenium named Conductor. https://github.com/conductor-framework/conductor. There you can find more complex example how to wrap WebDriver.
You perhaps need to be Extending EventFiringWebDriver. This class by its very behavior is composite in nature (Its created by taking a reference to an existing webdriver instance) and it was originally designed to be used for tapping into the before/after events of all webdriver originating actions. But it can very well suite your purpose.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.events.EventFiringWebDriver;
public class WrappedDriver extends EventFiringWebDriver {
public WrappedDriver(WebDriver driver) {
super(driver);
}
public void performCustomAction(){
//your customization code
}
}
So in essence you first build a decorator that implements all the interfaces that an actual RemoteWebDriver sub-class would implement and then have your customized class extend the decorator. The decorator class here in this case is EventFiringWebDriver
I am fairly new to Java so forgive me if this is a silly question, but believe me when I say I really cannot find a solid answer.
This is what I'm working with:
So I'm testing a program, and the easiest way to keep it maintained and updated is to create my own library of "buttons". Everything in the library are small functions like "enterValidCredentials" and "clickLoginButton".
So let's take a look at my test cases. In a perfect world I'd be able to just:
public class progressCheck {
public static void main(String[] args) {
WebDriver driver = new FirefoxDriver();
driver.get("http://mail.google.com/");
enterValidCredentials;
clickLoginButton;
}
}
enterValidCredentials and clickLoginButton exist in my library of classes. I know very well that that's not going to work as written above. What, literally, is the correct way to do this?
If it helps at all, my enterValidCredentials class looks like this:
public class loginPageButtons {
private WebDriver driver;
Actions actions = new Actions(driver);
public class enterValidCredentials { // This class enters in a valid username and valid password on the login page.
public void enterValidCredentials2() {
driver.findElement(By.cssSelector("input[type=\"text\"]")).clear();
driver.findElement(By.cssSelector("input[type=\"text\"]")).sendKeys("XXXXXXXX");
driver.findElement(By.cssSelector("input[type=\"password\"]")).clear();
driver.findElement(By.cssSelector("input[type=\"password\"]")).sendKeys("XXXXXXXX");
}
}
All my other functions follow a relatively similar structure (depending on their function, of course).
You can use a unit test to check single functionalities of your classes.
The most used library to create unit tests is JUnit.
If you use an ide (like IntelliJ or Eclipse) running the test can be done with a simple command exactly as running a main method.
If you need to create mocks of your objects you can use a library like Mockito (but there are many other valid alternatives).
Note: A mock is an object that has the same interface as a complex object that is difficult to use in a test environment (for example a db connection, a file handler, a network handler).
Here is an example, I tried to imagine your code and a possible test. I assumed that clickLoginButton returns an integer just to show a possible assert statement.
Example:
#Test
public static void testCredentials() {
WebDriver driver = new FirefoxDriver();
driver.get("http://mail.google.com/");
EnterValidCredentials enterValidCredentials = new EnterValidCredentials(); // Or create a mock if necessary
// Set values if necessary
int returnValue = enterValidCredentials.clickLoginButton();
assertEquals(returnValue, 1);
}
Hi I have following setup with my current framework,
ClassA
{
//Which Receives Selenium WebDriver call the 'driver' object reference to manipulate the locators in UI
public WebDriver get()
{
return MainClass.driver;
}
}
MainClass
{
public static Webdriver driver;
method A()
{
//which uses Firefox instance and it is passed to ClassA to operate
driver = new FirefoxDriver();
}
methodB()
{
//which creates new instance of Chrome
driver = new ChromeDriver();
}
}
What I wanted to do is once I call methodB() the instance of Chrome is created but once it is done I want to resume back to firefox instance that is available or invoked before chrome run, but with my approach since I am referring the same webdriver object the old firefox reference is getting deleted.
Any Suggestions ?
PS: Please forgive my bad code conventions I followed
Simplest solution would be to create seperate objects for FF and Chrome. Modify get method to take a parameter(browserType) and then return the correspoding object.
Why are you switching browsers?
You might want to see a different approach to your situation. I believe that if you have to use 2 browsers you are most likely trying to pass some info from one to the other. Here is how I see it:
ClassA
{
//Which Receives Selenium WebDriver call the 'driver' object reference to manipulate the locators in UI
public WebDriver get()
{
return MainClass.driver;
}
}
MainClass
{
public static Webdriver currentBrowser, firefoxInstance chromeInstance;
firefoxInstance = new FirefoxDriver();
chromeInstance= new ChromeDriver();
currentBrowser = firefoxInstance; //if you want start out with Firefox
currentBrowser()
{
return currentBrowser;
}
switchBrowser(Cookies passingInfo) //passingInfo could be like cookies but also just current page etc...
{
if(currentBrowser==firefoxInstance)
{
chromeInstance.cookies()=passingInfo; // this is definitely not the correct way of passing cookies in Selenium but you get my point
currentBrowser=chromeInstance;
}
else
{
firefoxInstance.cookies()=passingInfo;
currentBrowser=firefoxInstance;
}
}
}
Of course there are more than one way of doing this and it depends on what you end goal is. But keep in mind that some websites are designed differently depending on the user agent of the browser connecting to them and that might cause your code to crash (like i just experienced 2 minutes ago.) I recommend sticking to one web browser if you can.
PS: Please forgive me using your bad code conventions :)