However I'm having a hard time coming up with a real need for a Base
page object outside of that.
Remember You Ain't Gonna Need It: Solve your problem simply; to not try to find opportunities to create code. Code is trash, it gets complicated and costly to change. The less you build to solve your problem, the better.
Some people suggest storing "common" locators that all pages see
(Floating headers/footers/etc...) but isn't it better to store those
in components?
This may increase the level of coupling your code have. It's the typical case where you should prefer composition over inheritance and to improve the naming of your classes.
A master BasePage with components such as headers and footers will work until you have pages without headers, footers. You may ended, by mistake, using this BasePage in these different pages - it will compile and work, but your concrete page will have more behavior that necessary/capable of doing.
Try to create components that you can inject base locators/web elements, and they will have behavior hidden in the class.
E.g. here we have two instance of a... QuestionLinkList:

class QuestionLinkList {
QuestionLinkList(WebElement base) { this.base = base; }
public void clickOnLinkAt(Int position) {
base.findElements(ITEM_LOCATOR).get(position).click();
enter code here
}
}
Then on the QuestionPage, you can say you have two instances of this component:
class QuestionPage {
QuestionPage(WebDriver driver) {
this.relatedQuestions = new QuestionLinkList(driver.findElement(RELATED_QUESTIONS));
this.hotNetworkQuestions = new QuestionLinkList(driver.findElement(HOT_NETWORK_QUESTIONS));
}
}
With time, discovering similarities in the pages or duplications in your code, you can enhance your library of components, creating components of components, etc.
I would suggest using inheritance when you indeed discover specific types of pages or components.
For instance, you can have an abstract BasePage that holds a WebDriver and has a goto
method. You can have an abstract SearchablePage, that inherits from BasePage, and has a private search bar component and provide the method searchFor(String)
. And then you can the QuestionPage and the HomePage, both inheriting from SearchPage - because both are pages that you can search stuff over: You inherit the method/behavior, not the component.
utility classes
Here you may be creating spaghetti code. Here is a detailed explanation of why.