Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fetch Pagefactory WebElement name using reflection

I am using selenium pagefactory, and have introduced an annotation @Name(Description = "Username"), which I use for all WebElements. I need to find the value for Description in my custom methods later on, for reporting, like:

public static void click(WebElement element) throws Throwable {
        try {
            element.click();
        } catch(ElementNotInteractableException E1) {
            throw new UnableToInteractWithElementException("Unable To Interact With " + WebElement Descriptionn);
        }
    }

My @Name annotation interface and pagefactory look like this:

Name Interface

@Target({ElementType.FIELD, ElementType.TYPE,ElementType.CONSTRUCTOR})
public @interface Name {    
    String Description() default "";
}
@Name(Description = "Username")
@FindBy(id="txtLoginID")
public WebElement username;

The problem I face while using reflections is the need to define classname of pagefactory , and also provide fieldname as string "username" , to retrieve the annotation value.

I wish to be able to retrieve the annotation value by only providing my WebElement and no other object to my click(WebElement element) method.

like image 577
abhijit.bhatta Avatar asked Aug 23 '19 13:08

abhijit.bhatta


People also ask

How do you test web pages and initialize using pom?

Steps To Create Test Cases:Initialize the driver and open the application. Create an object of the PageLayer Class(for each webpage) and pass the driver instance as a parameter. Using the object created, make a call to the methods in the PageLayer Class(for each webpage) in order to perform actions/verification.

What is the use of PageFactory initElements?

Page Factory is a class provided by Selenium WebDriver to support Page Object Design patterns. In Page Factory, testers use @FindBy annotation. The initElements method is used to initialize web elements. Similarly, one can use @FindBy with different location strategies to find web elements and perform actions on them.

What is the diff between page Factory and pom?

In POM, you define locators using 'By' while in Page Factory, you use FindBy annotation to define page objects. Page Object Model is a design approach while PageFactory is a class which provides implementation of Page Object Model design approach.


2 Answers

I can show you how to get the description partially using reflection.

First of all, we have to fix the Annotation @Name because you didn't add RetentionPolicy:

@Target({ElementType.FIELD, ElementType.TYPE, ElementType.CONSTRUCTOR})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Name {
    String Description() default "";
}

Now, what I did and tested, we create storage for the names and we're gonna store them when initializing the Page Objects like this:

public class NameStore {
    public static HashMap<WebElement, String> map = new HashMap<>();

    public static void store(Object pageObject) {
        Field[] fields = pageObject.getClass().getDeclaredFields();
        for (Field field : fields) {
            Name annotation = field.getAnnotation(Name.class);
            if (annotation != null) {
                try {
                    map.put((WebElement) field.get(pageObject), annotation.Description());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static String getDescription(WebElement element) {
        return map.get(element);
    }
}

You pass a Page Object as an argument, @Name annotation is read and stored into the HashMap<WebElement, String>

Initialize name storing in a constructor:

    public PageObject(WebDriver driver) {
        PageFactory.initElements(driver, this);
        NameStore.store(this); //THIS IS CRUCIAL. Also make sure that's always AFTER PageFactory initialization
    }

And now, to update your click method:

public static void click(WebElement element) throws Throwable {
        try {
            element.click();
        } catch(ElementNotInteractableException E1) {
            throw new UnableToInteractWithElementException("Unable To Interact With " + NameStore.get(element));
        }
    }

Hope it helps!

Disclaimer: I didn't test it in the context of multi-threading. I did not test in the context of a constructor.

like image 134
Fenio Avatar answered Oct 04 '22 21:10

Fenio


Hi I'm not the pro with reflection, I'm not sure if I did get the question right. You may have some another optimal way of doing it, but follow one solution, create a method that receives the WebElement and return the description:

private static String getDescription(WebElement element) throws IllegalAccessException {
    //Get all the fields of the page object.
    for (Field field : YOUR_PAGE_OBJECT.class.getDeclaredFields()) {
        Name name = field.getAnnotation(Name.class);
        //Consider only the ones that is annotated by your @Name
        if (name != null) {
            WebElement classElement = (WebElement) field.get(element);
            //Get the element from the class and compare with your current.
            if (classElement != null && (classElement).equals(element)) {
                return name.Description();
            }
        }
    }
    return ":C"; // or throw something, whatever you want...
}

Your code will end like:

public static void click(WebElement element) throws Throwable {
        try {
            element.click();
        } catch(ElementNotInteractableException E1) {
            throw new UnableToInteractWithElementException("Unable To Interact With " + getDescription(element));
        }
    }
like image 44
Spencer Melo Avatar answered Oct 04 '22 20:10

Spencer Melo