I use Selenium Webdriver a lot, and there are a lot of "utility" methods I've written to make it easier for me to use. I put these classes in a WebDriverUtil
class, and now that file is over 1,200 lines long. Every method in WebDriverUtil
attempts to separate me from using WebDriver
because it's something I use a lot that wouldn't be DRY to keep writing.
For example, this is a method I would put in WebDriverUtil
.
public void waitUntilVisible(final WebElement webElement) {
new WebDriverWait(webDriver, 10).until(new Predicate<WebDriver>() {
@Override
public boolean apply(WebDriver webDriver) {
return webElement.isDisplayed();
}
});
}
If I have 1,200 lines of code full of methods like this, do I have a God object? If so, how could I fix it?
Should I separate my behavior into decorator classes like this?
public class WebElementDecorator implements WebElement {
private WebElement webElement;
private final WebDriver webDriver;
public WebElementDecorator(WebElement webElement, WebDriver webDriver) {
this.webElement = webElement;
this.webDriver = webDriver;
}
public void waitUntilVisible() {
new WebDriverWait(webDriver, 10).until(new Predicate<WebDriver>() {
@Override
public boolean apply(WebDriver webDriver) {
return webElement.isDisplayed();
}
});
}
public void click() {
webElement.click();
}
//... other WebElement methods
}
If I have 1,200 lines of code full of methods like this, do I have a God object?
The number of lines of codes alone isn't an adequate indicator on whether a class is god-like or not. A class can be bloated with codes due to bad coding styles, over-engineering, different variants of over-specialized methods, a verbose language, inlined comments etc.
A god class is one that is bloated with responsibilities. Here are two litmus tests to determine if your util class has evolved into a god class:
The effects on your util class when changing your tests. If changes to a subset of your tests causes you to change and re-compile your util class often, then it's probably an indication that your util class is serving too many masters. The ideal scenario is that changes to a subset of your tests will only affect those util methods (if necessary) that are directly relevant to the tests.
The effects on your test class when changing your util class. If changing part of your util class causes unexpected failures among many unrelated tests, then your util class might have spread its tentacles all over your tests.
If so, how could I fix it? Should I separate my behavior into decorator classes like this?
It's best to start refactoring in small, incremental steps. Using your code example, I will start off by simply extracting all Wait..Until..Predicate
codes into a separate class named WaitUntilEvent()
or something, with methods like isVisible()
, isEnabled()
, isSelected()
etc. An example usage looks like this:
WaitUntilEvent waitUntil = new WaitUntilEvent(webElement, webDriver);
waitUntil.isVisible();
webElement.click();
// etc..
If I ever need to change my test requirements around Wait..Until..Predicate
(such as the timeout intervals), I know there is only one class to edit. This can then be refactored further into until(PredicateIsTrue).then(PerformAction)
, until(FunctionIsTrue).then(PerformAction)
etc. I like this approach better than an all-encompassing god-like class WebElementDecorator
which will likely end up with many decorating methods capturing many different behaviors.