Implementing the Page Object Model - automation

I am building a test automation project using Perl and Selenium. I am using the Page Object Model. I am somewhat unsure about where the Selenium driver would fit into the implementation of the page object model.
Should each page object 'have' a driver ? The way I am thinking is that each page object represents a set of services that the page offers to a user. With this concept in mind , a page object does not have a 'has-a' relationship with a driver. A page object interacts with a driver. However, I am still looking for suggestions. Should I have the driver as part of each page object in the web-application?
Thanks!

This answer won't be any much different from #zodvik, and your thought process, but is another optional approach. Instead of passing the driver around, you can create an abstract class that each page object can then inherit from. From the abstract class, can also contain some common functional methods that you will find yourself often using.
This is at least how I do it in Java language.

I always include driver as part of every Page Object. The way I thought about driver was that it represents the state of the current page. It gives access to the URL, Page Source, etc.
Now, each page has a a current URL, a page source code, a page title which are all accessible through the driver.

In the way, I implemented the framework. I used the driver in the commonFactory.class that contains commonly used elements. Each page was implemented as a child class of the commonFactory.class. so you don't have to implement the driver in each and every class. Since the driver is independent of the test scenarios it's better to have it in a separate way.

As I understand, there are No set rules/standards for implementing POM.
However, the general thumb rule is to create a BaseTest and BasePage class in your Framework where each respective webpage (like Login) will represent by its PageClass(LoginPage).
Similarly, all your Page classes will extend your BasePage and all tests will extend your BaseTest class.
Below is a rough idea of its implementation ->
public class BaseTest{
#BeforeSuite()
setupMethod(){
initialize your WebDriver here
}
}
------------------------------------------
public class BasePage {
//create constructor
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(this.driver, Duration.ofSeconds(TIMEOUT));
PageFactory.initElements(new AjaxElementLocatorFactory(this.driver, TIMEOUT), this);
}
//other common methods which can be utilized in your respective child Page classes
}
----------------------------------------------------
public class LoginPage extends BasePage {
//Your Locators and Weblelements
private static final LOGIN_ID = "login";
//Constructor to supply webdriver
public LoginPage(WebDriver driver) {
super(driver);
}
//your action methods
public void loginToApp(){
driver.findbyelement(By.ID(LOGIN_ID)).click
}
}
----------------------------------------------------
public class LoginTest extends BaseTest{
public LoginPage login;
BeforeAll()
{
login = new LoginPage(driver);
}
#Test
public void verifyLogin(){
login.loginToApp();
}
}

Related

How to create a BaseClass that adds logging messages

I am using serenity BDD for my automation testing and Page Object Model for my framework. I have created a BasePage class which will be inherited by all the other Pages. I want to minimize the logging messages from the Pages by adding all the log.info messages to a central Base page. Example, when calling the click() method, I will log before click and after click methods as shown below in the basePage class:
public class BasePage extends PageObject{
private static final Logger log = LogManager.getLogger(BasePage.class.getClass());
private final WebElementFacade element;
public static void clickBtn(WebElementFacade btnName) {
log.info("About to click " + btnName + " button");
btnName.click();
log.info("Successfully clicked on " + btnName + " button.");
}
Later I figured that instead of individually trying to figure out in advance what actions the user will perform on the webElements, and write new methods for each action (like the one shown above), just implement WebDriverFacade interface, so that I get all the unimplemented method list in BasePage from WebDriverFacade and then write the log messages inside each of them, like so:
public class BasePage extends PageObject implements WebElementFacade{
private static final Logger log = LogManager.getLogger(BasePage.class.getClass());
private final WebElementFacade element;
#Override
public void submit() {
// TODO Auto-generated method stub
}
#Override
public void sendKeys(CharSequence... keysToSend) {
// TODO Auto-generated method stub
}
#Override
public String getTagName() {
// TODO Auto-generated method stub
return null;
}
#Override
public boolean isSelected() {
// TODO Auto-generated method stub
return false;
}
.
.
.
.
.
}
This will serve two purposes:
I will not have to create new methods for every action in BasePage class, example the 'clickBtn()' function in the first code
As I mentioned before, I will not have to figure out what method any other person who adds methods to my code might use and having to change the BasePage class to create the new actions. So basically less maintenance in the long run.
The problem I am facing is an error that I receive in the second use case:
The return types are incompatible for the inherited methods WebElementFacade.withTimeoutOf(int, TimeUnit), PageObject.withTimeoutOf(int, TimeUnit)
Now my question is:
How can solve this problem?
Is this the right way to do things or should I be going with the first method and have maintenance overhead.
Just figured that another scenario where this might be useful. To make sure that subclass methods do not use methods from pageObject and are forced to use the methods from BaseClass only. This can be done by wrapping the WebElementFacade and adding the log messages as an added functionality. Any thought on this would be appreciated.
Thank you!
Honestly it is a neat trick and if you get it working you should be proud.
I think I figured something similar out in a dynamic language.
But you are better off just adding the logging entries and learning the following.
How to name functions so you don't feel like they need renaming.
How to log clearly for debugging use.
This is because loggings power is in its flexibility.
When you learn how to dump something complex like a matrix so you can eye it and go uh-oh you are increasing your overall skills.
I apologize for not giving you code but I felt some "chase the other rabbit" advice was better.

How did TestNg annotation mentioned in one class get executed from another from another class?

While learning Testng on Udemy, I come across a code which I am unable to understand. Instructor has created class named "testcore" where he defined #BeforeMethod/#aftermethod.Later he created another class named "LoginTest" where he wrote actual test with #test. He extended testcore class in loginTest to get variable initiated in testcore class. When he ran loginTest then #BeforeMethod/#aftermethod also ran with this. How did these two method ran along with #test when these methods are in different class.
here are both codes:
public class testcore {
public static Properties config = new Properties();
public static Properties obj = new Properties();
public static Xls_Reader xls = null;
public static WebDriver driver;//=null;
#BeforeMethod
public void init() throws Exception{
if(driver==null) {
// Loading Config Properties File
File Config_f = new File(System.getProperty("user.dir")+"\\src\\dd_Properties\\config.properties");
FileInputStream fs = new FileInputStream(Config_f);
config.load(fs);
// Loading Object Properties File
File Obj_f = new File(System.getProperty("user.dir")+"\\src\\dd_Properties\\Object.properties");
fs = new FileInputStream(Obj_f);
obj.load(fs);
//Loading xlsx file
xls = new Xls_Reader(System.getProperty("user.dir")+"\\src\\dd_Properties\\Data.xlsx");
System.out.println(config.getProperty("browerName"));
if(config.getProperty("browerName").equalsIgnoreCase("Firefox")) {
driver = new FirefoxDriver();
}
else if(config.getProperty("browerName").equalsIgnoreCase("Chrome")) {
driver = new ChromeDriver();
}
else {
throw new Exception("Wrong/No Browswer sepcified");
}
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
}
#AfterMethod
public void quitdriver() throws EmailException {
driver.quit();
//monitoringMail.Sendmail();
}
Here are LoginTest class:
public class LoginTest extends testcore {
#Test
public void doLogin() {
driver.findElement(By.xpath(obj.getProperty("LoginBtn"))).click();
driver.findElement(By.xpath(obj.getProperty("username"))).sendKeys();
driver.findElement(By.xpath(obj.getProperty("password"))).sendKeys();
}
1) How did these two method ran along with #test.
First of all LoginTest extends the testcore .
Due to this we can inherit the method of testcore in our LoginTest class.
2) When he ran loginTest then #BeforeMethod/#aftermethod also ran with this.
Please refer below details
Configuration information for a TestNG class:
#BeforeSuite: The annotated method will be run before all tests in this suite have run.
#AfterSuite: The annotated method will be run after all tests in this suite have run.
#BeforeTest: The annotated method will be run before any test method belonging to the classes inside the <test> tag is run.
#AfterTest: The annotated method will be run after all the test methods belonging to the classes inside the <test> tag have run.
#BeforeGroups: The list of groups that this configuration method will run before. This method is guaranteed to run shortly before the first test method that belongs to any of these groups is invoked.
#AfterGroups: The list of groups that this configuration method will run after. This method is guaranteed to run shortly after the last test method that belongs to any of these groups is invoked.
#BeforeClass: The annotated method will be run before the first test method in the current class is invoked.
#AfterClass: The annotated method will be run after all the test methods in the current class have been run.
#BeforeMethod: The annotated method will be run before each test method.
#AfterMethod: The annotated method will be run after each test method.
It's very simple inheritance related question. When you have extended testcore class in LoginTest class all the methods and data member available in parent will be available to child class including methods annotated with #Before Method etc.
I think you are confused due to miss concept regarding the way TestNG run a program. There are different different way to run a testNG annotated program to get executed and among them XML is a way. So don't get get confuse that all class, methods need to include in that xml. You just need an entry point only and rest will call accordingly.

Where to cause initElements method using FindBy annotation

I'm trying to use page object model but I'm not sure how to init elements correctly.
Please see an example of my test class:
public class TestSuiteSubscriber extends TestInitializer {
Menu menuPage = new Menu(driver);
SubscribersSearchForm searchForm = new SubscribersSearchForm(driver);
#Test (priority = 1)
public void findByOldNumber(){
menuPage = PageFactory.initElements(driver, Menu.class);
menuPage.openSubscribers();
searchForm = PageFactory.initElements(driver, SubscribersSearchForm.class);
searchForm.subscriberNumber.sendKeys("100001");
}
If I cause .initElements in constructors of page classes, I get the NullPointerException. As I understand, this is because they had been initialized before the page is loaded. Is there a proper way to avoid causing the .initElements method directly in the test method to make tests more readable?
A very common practice of instantiating the elements using PageObject is to use a BaseClass and inherit that throughout the other page classes. So the initElements will be instantiated every time the PageObject is instantiated. I have GitHub where I implemented the concept with TestNG. See if that would help you.
In your case TestInitializer should have the initElements I believe. This line should reside in TestInitializer constructor
PageFactory.initElements(driver, this);

getting a 'The IWebDriver object must implement or wrap a driver that implements IHasInputDevices.' error

im a newbie in c# and selenium.
Im trying to create an element extension to mouseover an element.
I've got the following:
public static void mouseoverElement(this IWebElement element, IWebDriver driver)
{
Actions actions = new Actions(driver);
actions.MoveToElement(element).Perform();
}
And this would be called from another class
public MLinks mouseOverCandidate()
{
candidateMenu.mouseoverElement(driver);
return this;
}
And this is where i will call from the test:
new HomePage(driver, server)
.MainLinks.mouseOverCandidate();
I will always get this which i don't quite understand. I've already got a driver set. Anyone can help me out on this? thanks
System.ArgumentException : The IWebDriver object must implement or wrap a driver that implements IHasInputDevices.
The method mouseoverElement is taking an IWebDriver, which is an interface.
The concrete driver class implements this interface, as well as the other interface IHasInputDevices.
So you need to pass in the concrete class, so that it can expose IHasInputDevices, as well as IWebDriver
Note that you will also get this error if the WebDriver you pass in is null.

wicket and AtUnit

I've started playing with Wicket and I've chosen Guice as dependency injection framework. Now I'm trying to learn how to write a unit test for a WebPage object.
I googled a bit and I've found this post but it mentioned AtUnit so I decided to give it a try.
My WebPage class looks like this
public class MyWebPage extends WebPage
{
#Inject MyService service;
public MyWebPage()
{
//here I build my components and use injected object.
service.get(id);
....
}
}
I created mock to replace any production MyServiceImpl with it and I guess that Guice in hand with AtUnit should inject it.
Now the problems are:
AtUnit expects that I mark target object with #Unit - that is all right as I can pass already created object to WicketTester
#Unit MyWebPage page = new MyWebPage();
wicketTester.startPage(page);
but usually I would call startPage with class name.
I think AtUnit expects as well that a target object is market with #Inject so AtUnit can create and manage it - but I get an org.apache.wicket.WicketRuntimeException: There is no application attached to current thread main. Can I instruct AtUnit to use application from wicketTester?
Because I don't use #Inject at MyWebPage (I think) all object that should be injected by Guice are null (in my example the service reference is null)
I really can't find anything about AtUnit inside Wicket environment. Am I doing something wrong, am I missing something?
I don't know AtUnit but I use wicket with guice and TestNG. I imagine that AtUnit should work the same way. The important point is the creation of the web application with the use of guice.
Here how I bind all this stuff together for my tests.
I have an abstract base class for all my tests:
public abstract class TesterWicket<T extends Component> {
#BeforeClass
public void buildMockedTester() {
System.out.println("TesterWww.buildMockedTester");
injector = Guice.createInjector(buildModules());
CoachWebApplicationFactory instance =
injector.getInstance(CoachWebApplicationFactory.class);
WebApplication application = instance.buildWebApplication();
tester = new WicketTester(application);
}
protected abstract List<Module> buildModules();
The initialization is done for every test class. The subclass defines the necessary modules for the test in the buildModules method.
In my IWebApplicationFactory I add the GuiceComponentInjector. That way, after all component instantiation, the fields annotated with #Inject are filled by Guice:
public class CoachWebApplicationFactory implements IWebApplicationFactory {
private static Logger LOG = LoggerFactory.getLogger(CoachWebApplicationFactory.class);
private final Injector injector;
#Inject
public CoachWebApplicationFactory(Injector injector) {
this.injector = injector;
}
public WebApplication createApplication(WicketFilter filter) {
WebApplication app = injector.getInstance(WebApplication.class);
Application.set(app);
app.addComponentInstantiationListener(new GuiceComponentInjector(app, injector));
return app;
}
}