Component objects instead of page objects in selenium - selenium

One of the most popular patterns in testing based on selenium is page object. Unfortunately, we often need to duplicate code if we use it as is. Consider the following situation:
We use UI framework, with common component, say some fancy table
Table is quite complicated, has filtering, sorting, searching
The table is used on several pages in the app
Is there any existing infrastructure to create more granular component objects instead of page objects either in selenium or in a thrid party lbirary?. I mean, annotations, and related infrastructure?

Appium which is the mobile implementation of selenium webdriver has a concept of widgets which is an extension of pageobjects. There is a Widget class which allows one to search relative to an element including in a web browser. You can check this out in the appium source test section. Have a look in the package io.appium.java_client.pagefactory_tests.widgets. This supports the FindBy annotation and WebElement construct and the PageFactory initialization.
So instead of using
#FindBy(.......)
private WebElement myTable;
you can use
#FindBy(container of the table....)
private Table myTable;
Table class can now have all the relevant variables and methods.
Part of POM.xml
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-api</artifactId>
<version>2.53.1</version>
</dependency>
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>4.1.2</version>
</dependency>
Test Class --
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.PageFactory;
public class WidgetBrowTest {
#Test
public void test() {
System.setProperty("webdriver.chrome.driver", "E:/Software Testing/Selenium/Jars/chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.get("http://stackoverflow.com/");
SOHome soHome = new SOHome(driver);
PageFactory.initElements(new AppiumFieldDecorator(driver), soHome);
//Below two are from widget - First question in stackoverflow homepage
System.out.println(soHome.getFirstQues().getQuesTitle());
System.out.println(soHome.getFirstQues().getQuesTags());
//Below two are from home page
System.out.println(soHome.getLogoText());
System.out.println(soHome.getMenuText());
}
}
StackOverflow Home -
import java.util.List;
import java.util.stream.Collectors;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class SOHome {
#FindBy(css="div[id='hlogo'] > a")
private WebElement logo;
#FindBy(xpath="//div[#id='hmenus']//li/a")
private List<WebElement> menuOpt;
#FindBy(css="div[class='summary']")
private SOQuesWidget firstQues;
private WebDriver driver;
public SOHome(WebDriver driver) {
this.driver = driver;
}
public String getLogoText() {
return logo.getText();
}
public List<String> getMenuText() {
return menuOpt.stream().map(t -> t.getText()).collect(Collectors.toList());
}
public SOQuesWidget getFirstQues() {
return firstQues;
}
}
Question Widget - First Question
import java.util.List;
import java.util.stream.Collectors;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import io.appium.java_client.pagefactory.Widget;
public class SOQuesWidget extends Widget {
#FindBy(css="a[class='question-hyperlink']")
private WebElement quesTitle;
#FindBy(xpath=".//div[starts-with(#class,'tags')]/a")
private List<WebElement> quesTags;
protected SOQuesWidget(WebElement element) {
super(element);
}
public String getQuesTitle() {
return quesTitle.getText();
}
public List<String> getQuesTags() {
return quesTags.stream().map(t -> t.getText()).collect(Collectors.toList());
}
}

Page objects is kind of a misnomer. They don't have to be specifically full pages to follow the page object model. I would create a Table class (page object) that contains all of the locators and methods for the Table object and then include it in the pages/page objects where it appears.
For example, if the home page contains a table object, then the HomePage class would reference the Table class.

Related

Why doesn't intellij suggest org.mockito.Mockito dependency when using verify()?

When creating a new test, I have issues importing mockito methods into the class.
The class looks like
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = MainSpringBootApplication.class)
class CoolServiceTest {
#Autowired
SomeService someService;
#Test
void testGetInstitutionsCache_ShouldNotSecondTime() {
assertEquals(someService.getName(), "bob");
verify(idsClientMock, times(1));
}
}
In the import menu of intellij I don't see the option to import org.mockito.Mockito which I know where the package resides.
Instead I get the option to add;
import static org.testcontainers.shaded.com.google.common.base.Verify.verify;
(obviously not what I want)
I can get around it by typing the full Mockito.verify and then adding an on-demand static import to tidy the code.
The package is definitely installed and available to use, but intellij won't pick it up automatically.

Nulls and Empty collections are stored in the database of my custom mapper

I have a custom ObjectMapper that deserializes Product and Order objects. It works fine but it stores and returns nulls in the Mongodb database. Here is my configuration. In my implementation of the custom serializers, I return an empty list/map for empty collections.
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.stereotype.Component;
import java.text.DateFormat;
import java.util.Locale;
#Component("retailObjectMapper")
public class RetailObjectMapper extends ObjectMapper {
public RetailObjectMapper() {
setDateFormat(DateFormat.getDateTimeInstance(DateFormat.DEFAULT,
DateFormat.DEFAULT, Locale.US));
SimpleModule module = new SimpleModule();
module.addDeserializer(Product.class,new ProductDeserializer(Product.class));
module.addDeserializer(Order.class,new OrderDeserializer(Order.class));
setSerializationInclusion(JsonInclude.Include.NON_NULL);
setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
registerModule(module);
}
}
Unfortuantely, the fields not populated return null and empty collections also return null. I have no idea why this is happening, help please.

Cannot invoke "org.openqa.selenium.WebDriver.manage()" because "this.driver" is null

I have created two classes. One class am using for webdriver initialization and in another class am extending the properties of my first class so that I don't have to initialize my webdriver again and again and after that using that web driver object (from the first class) am trying to perform some actions. Now my chrome is initialized but further, it is performing no actions in it and it is showing Cannot invoke "org.openqa.selenium.WebDriver.manage()" because "this.driver" is null.
Please refer my screenshots below:
[class 1][1]
[class 2][3]
[File ][2]
[3]: https://i.stack.imgur.com/jBOnJ.png`import java.io.IOException;
Class 1
import org.testng.annotations.Test;
public class project1 extends project1classB {
#Test
public void basePageNavigation() throws IOException
{
driver = initializeDriver();
driver.get("http://www.demo.guru99.com/V4/");
}
}`````
`
class 2
````import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class project1classB {
public WebDriver driver;
public WebDriver initializeDriver() throws IOException
{
Properties prop = new Properties();
FileInputStream fil= new FileInputStream("C:\\Users\\mimo\\eclipse-workspace\\Guru99Assignment\\src\\data.properties");
prop.load(fil);
String browserName = prop.getProperty("browser");
if(browserName.equals("chrome"))
{
System.setProperty("webdriver.chrome.driver", "C:\\Users\\mimo\\Desktop\\chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.get("http://www.demo.guru99.com/V4/");
}
else if(browserName.equals("firefox"))
{
}
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
return driver;
}
}````
[1]: https://i.stack.imgur.com/QQnyn.png
[2]: https://i.stack.imgur.com/OHOda.png
You did not provide your code, but from the error you received it's clear that you did not initialize the driver object instance.
I resolved the issue by myself what I was doing wrong here was initializing my Webdriver two times in my class 2 due to which it was giving this error.
Issue sloved
I also had this kind of issue.
(1). Check whether excel sheets whether having null data.
(2). I added border to the whole column in excel and again checked with removing those borders and issue was solved.
(3). Excel column borders adding this issue BE CAREFUL WHEN ADDING BORDERS.
enter image description here

How to use before in cucumber

How can I use "before" to create the driver instance and launch Firefox in cucumber feature file.
I am familiar with background but never used before.
This example is taken from ToolsQA
Let’s do some easy and small example of Cucumber Hooks just to understand the concept.
Feature: Test Hooks
Scenario: This scenario is to test hooks functionality
Given this is the first step
When this is the second step
Then this is the third step
Step Definitions
package stepDefinition;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
public class Hooks_Steps {
#Given("^this is the first step$")
public void This_Is_The_First_Step(){
System.out.println("This is the first step");
}
#When("^this is the second step$")
public void This_Is_The_Second_Step(){
System.out.println("This is the second step");
}
#Then("^this is the third step$")
public void This_Is_The_Third_Step(){
System.out.println("This is the third step");
}
}
****Note***: There is no logic used in the step definitions. Just printing the step summary log.*
Now hooks come in the picture, in case of your scenario, you will like to initialize the driver here.
Hooks
package utilities;
import cucumber.api.java.After;
import cucumber.api.java.Before;
public class Hooks {
#Before
public void beforeScenario(){
System.out.println("This will run before the Scenario");
}
#After
public void afterScenario(){
System.out.println("This will run after the Scenario");
}
}
Make sure that the package import statement should be import cucumber.api.java.After; & import cucumber.api.java.Before;
Often people mistaken and import Junit Annotations, so be careful with this.
Output
Aparts from hooks, you can make use of other usefull annotations in Cucumber, please refer the tutorial on ToolsQA.com here.

Pros and Cons of using Own Selectors in selenium

I would like to implement my Own By Class in order to have custom selectors. What are the pros and cons of using Custom Selectors
DEV code
<button class="btn js-AddNuts" type="button" testid="addbutton">
Here Selector is testid
Reason: We are planning to have test specific selectors and design Names for all the elements in DEV Code so that if they change anything in DEV it doesn't impact test.
There is no need to create a custom locator in this case (or any case that I have run across or can think of). You can simply use the following code that uses a CSS Selector.
By.cssSelector("button[testid='addbutton']")
I would suggest that you spend some time reading and learning CSS Selectors. They are very powerful.
CSS Selector Reference
CSS Selector Tips
As you know there is no problem with creating your own locator, Selenium provides the functionality to create custom locator. Basically selenium internally uses to locate element using xPath when you are going to find element By.id, By.name etc. So you could create simply your own locator by extending By class.
If you want to create a custom locator which locate element using testid, you should create as below (Assuming you are using java) :-
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;
class ByTestId extends By {
private final String testId;
public ByTestId(String testId) {
this.testId = testId;
}
#Override
public List<WebElement> findElements(SearchContext context) {
return context.findElements(By
.xpath(".//*[#testid = '"+testId+"']"));
}
#Override
public WebElement findElement(SearchContext context) {
return context.findElement(By
.xpath("//*[#testid = '"+testId+"']"));
}
}
Now you can easily locate element by using testid as below :-
//for single webelement
WebElement element = driver.findElement(new ByTestId("addbutton"));
//for list of webelements
List<WebElement> elements = driver.findElements(new ByTestId("addbutton"));
Note : You can also locate the element in your custom By class using By.cssSelector as : By.cssSelector("*[testid = '"+testId+"']")
Hope it helps..:)