1

I'm working on webtests at the moment, using a WebDriver to simulate a browser. This WebDriver has to find HTML elements pretty often, many of them often being the same (i.e. a certain button, form field, etc.). The WebDriver needs an object of Selenium's By class, to use findElement().

I figured it would be nice for readability and adaptability, if there was an enum or interface storing configured instances of By, which may be used to locate the element. In tests, repeatedly hard-coding findElement(By.id("elementid")) turns into findElement(WebGui.Login.SUBMIT_BUTTON), where WebGui.Login.SUBMIT_BUTTON may return an instance of By searching for an ID, name, CSS class, etc. whatever is neccessary without concerning the test itself which just needs the element.

Question A hierarchical structure seems optimal to represent different sections of the website, however enums can't inherit anything. Which way is the most clean way and/or best practice?

Nested Enum As enums can't inherit getBy() and the constructor, so they must be defined in every nested enum, thus being more redundant code.

public enum WebGui {
    BUTTON1(By.id("button1")),
    SEND_BUTTON(By.name("sendButton"));

    private By searchCriteria;

    WebGui(By by) {
        searchCriteria = by;
    }

    public By getBy() {
        return searchCritera;
    }

    private enum SubPage {
        BUTTON2(By.id("button2")),
        SEND_BUTTON(By.linkText("Send"));

        private By searchCriteria;

        WebGui(By by) {
            searchCriteria = by;
        }

        public By getBy() {
            return searchCritera;
        }
    }
}

Nested Interfaces/abstract classes Using this seems less "clean", doesn't provide type safety (although not too relevant here) and is often refered to as bad practice, because storing a fixed set of available constants is an enums purpose.

Edit Nested Interfaces can't be private, therefore changed outer interface to abstract class

public abstract class WebGui {
    By BUTTON1 = By.id("button1");
    By SEND_BUTTON = By.name("sendButton");

    private interface SubPage {
        By BUTTON2 = By.id("button2");
        By SEND_BUTTON = By.linkText("Send");
    }
}

As nested Interfaces are much less code and its members don't need functions, using it seems to be the better choice, but how much of a bad practice is this, compared to nested enums containing redundant code.

GxTruth
  • 509
  • 4
  • 9
  • In your first example - why is that inner enum *private*? – GhostCat Aug 14 '17 at 09:29
  • Why nest the interfaces/classes structurally? How about `class WebGui { ... public final SubPage1 subPage1 = new SubPage1(); }`? – JimmyB Aug 14 '17 at 09:32
  • @GhostCat I tried to avoid the inner enums to be accessible from outside, without using WebGui.Login, as they only make sense in WebGui-context. However, this turned out to not work as intended... – GxTruth Aug 14 '17 at 10:53
  • @JimmyB This still requires the class to be visible (public), in which case I can also but the class itself into WebGui (static inner class). I tried to implement a solution which does not expose the inner enum/class to all classes, except they use WebGui.InnerClass. Seems like this is not possible the way I intended it :/ – GxTruth Aug 14 '17 at 10:57
  • Not sure I fully understand you, but to clarify my idea: WebGui becomes a singleton. Then you access all data via `webGuiSingleton.subPage1.xyz`. Unless someone manually creates an instance of e.g. `SubPage1` and uses it, the 'API' seems to be pretty fool-proof. – JimmyB Aug 14 '17 at 11:14
  • Thanks for the idea @JimmyB. My idea of an optimal solution was a hierarchie whose nested classes/enums are hidden from the outside, only accessible using `OuterClass.InnerClass.ELEMENT` (`InnerClass.ELEMENT` is supposed to be impossible), but I played around a little more and it turns out, it does not work the way I intended. Probably gonna use the Interface/Class approach as it's more readable and type safety is not really required (type of the members themselves is important). If I come across a more elegant solution, I'll edit my posts. – GxTruth Aug 14 '17 at 11:30

1 Answers1

0

You can have the enum implement an interface like my answer to this question.

Ray Tayek
  • 9,841
  • 8
  • 50
  • 90