Search code examples
javaseleniumtestingcucumberbdd

Want to use Enum file name dynamically to fetch constants


I have a config file with key value pair as language = "IN" and i have multiple page object enum files with name as PageObject_US,PageObject_UK,PageObject_IN

every page object enum file has constants that can be accessed using for example PageObjects_US.String.lable

but what i want to achieve is a way to create something like below

  1. take the parameter from config file store it in a string like String language = "IN"
  2. Then concatenate using "PageObjects_" + language to get (PageObjects_IN)
  3. so that the returned value can be used to fetch the constants from PageObjects_IN.String.label.

following is the code block:


if(!ENV.equalsIgnoreCase("development") && VALIDATION.equalsIgnoreCase("yes")) {        
            Elements.ByTitle(webDriver,PageObjects_IN.GREAT.label);
            Elements.ByID(webDriver,  PageObjects_IN.COUNTER.label);
}

In the above i want to use enum file PageObjects_IN at run time as i have many enum files

below is the enum

public enum PageObjects_IN {
    // Text
    GREAT("great"), 
    COUNTER("counter");


    public final String lable;

    PageObjects_IN(final String lable) {
        this.lable = lable;
    }

}

Solution

  • This is possible (using reflection) but strongly not recommended as it eliminates the efficiency of Java language constructs.

    Not recommended way

    Say you have a package click.webelement.cucumber.po where you store

    public enum PO_EN {
        GREAT("great_en"),
        COUNTER("counter_en");
    
        public final String label;
    
        PO_EN(String label){
            this.label = label;
        }
    }
    

    and

    public enum PO_IN {
        GREAT("great_in"),
        COUNTER("counter_in");
    
        public final String label;
    
        PO_IN(String label){
            this.label = label;
        }
    }
    

    Then to take a value you can do something like this:

    String lang = "EN";
    // Take class
    Class clazz = Class.forName("click.webelement.cucumber.po.PO_" + lang);
    // Find an object that represent enum constant
    Object val = Arrays
            .stream(clazz.getEnumConstants()).filter(o -> "GREAT".equals(o.toString()))
            .findAny()
            .get();
    // Take field value for that object
    Field f = clazz.getField("label");
    System.out.println(f.get(val));
    

    This is error-prone approach and you would not have benefit from compile phase.

    Recommended approach - 1

    Instead of having enum use classes.

    public abstract class PO {
    
        public abstract String great();
        public abstract String counter();
    
    }
    

    and

    public class PO_EN extends PO{
        @Override
        public String great() {
            return "great_en";
        }
    
        @Override
        public String counter() {
            return "counter_en";
        }
    }
    

    and

    public class PO_IN extends PO{
        @Override
        public String great() {
            return "great_in";
        }
    
        @Override
        public String counter() {
            return "counter_in";
        }
    }
    

    so your test would be much simpler

    String lang = "EN";
    Class clazz = Class.forName("click.webelement.cucumber.po.PO_" + lang);
    PO val = (PO) clazz.getDeclaredConstructor().newInstance();
    System.out.println(val.great());
    

    Recommended approach - 2

    You can utilize PageFactory harness for your page objects and use this lib to parametrize your locators, like (if you use test ng):

    @DataProvider(name = "languages")
    Object[][] dataProvider(){
        return new Object[][]{
                {"en", "great_en", "counter_en"},
                {"in", "great_in", "counter_in"}
        };
    }
    
    @Test(dataProvider = "languages")
    public void testPage(String language, String great, String counter){
        DefaultParameterProvider
                .properties
                .set(Map.of("p.great", great, "p.counter", counter));
        MyPage myPage = new MyPage(driver);
        ...
    }
    

    Where your page would be like this:

    public class MyPage extends PageObjectParameterized {
    
        @FindByParameterized(xpath = "//button[@name='{wec:p.great}']")
        WebElement great;
    
        @FindByParameterized(xpath = "//label[text()='{wec:p.counter}']")
        WebElement counter;
    
        @FindBy(xpath = "//input")
        WebElement input;
        
        public MyPage(SearchContext searchContext) {
            super(searchContext);
        }
    }