Refactoring a Framework

Assembling the pieces needed to turn our scripts into an efficient engine.


It feels like we’re on the cusp of having something here, you know? We have a handful of scripts that can do all of our basic operations, but it still feels a little disorganized. If we wanted to re-structure our existing functions, where could we move them?


Single Responsibility Classes

At the end of the last article, we noted that the driver was an object that felt out of place. If we wanted to re-structure our existing functions, where could we move them?

A visual overview of all of the functions that we have so far.

There is a concept in programming called the single-responsibility principle, which can be boiled down to functions and classes focusing on doing something specific. If we want each .java file to do one thing, then WikipediaTests and RadioButton are doing their job well. All of the tests are in the WikipediaTests class, and all of the radio menu code is in the RadioButton class. SeleniumFunctions, on the other hand, seems to be a place that we’re putting everything else. Let’s look deeper.

All of the functions in SeleniumFunctions have a void return type except for the RadioButton functions. Why is that? The purpose of SeleniumFunctions is to hold actions – search, navigate menus, etc. Although the RadioButton functions allow us to change the settings, what is really doing that operation is on the RadioButton class. So these functions are acting more like bookmarks. Did you need to reference that? Here’s how to get there. What if we had a model for all of these “bookmarks” on the page? Some sort of page model object?

Lets create a class called WikipediaPage.java and move over those RadioButton functions. The red squiggles immediately reminds us that we need to add a WebDriver driver to make this class whole. In order for it to be valid, we’ll need a WikipediaPage constructor that accepts a driver as well.

Something like this.

Now we have to decide how and where this class gets created. We already know that it needs to have a WebDriver, so it could be on SeleniumFunctions, right? We could create this WikipediaPage object there, right after we create the driver. Lets try it out and see:

If the page isn’t public, it can’t be accessed in the test.

Then, once we update the test, we can run it to verify the change is valid:

Another green check!

This works! We’ve wired things up correctly. However, we’ve also setup a pattern where anytime we need to pass the driver to a new object, it has to happen inside of the OpenBrowser function. This will quickly become a bottleneck, and this pattern starts to get in the way of the single responsibility of SeleniumFunctions. If we need something to be passing around the WebDriver for other classes to use, we need to create a WebDriver manager class.

So, with courage, lets make another class called DriverManager.java. It needs to have a WebDriver class variable, and let’s start by deleting the OpenBrowser() function from SeleniumFunctions.java and moving it into DriverManager.java.

Technically the “this.” calls aren’t necessary, they just help readability.

Since this class’s responsibility is to provide the WebDriver to other classes, we can change the OpenBrowser() function into a constructor that sets the driver, and make the driver member class public. You can remove the page object setup as well. We’ll need to remove the sleep statement and that throws InterruptedException stuff as well, because constructors can’t (shouldn’t) have exceptions like that. You can add a line instead that manages the webdriver’s default timeout value or explore other waiting strategies.

We can keep the URL in the constructor, there’s no reason to remove it.

Since we lost our reference to page, we’ll need to decide where that gets declared instead. Where might that be, in an ideal world? The classes don’t need to know much about each other, so perhaps the place where these classes meet, the tests file, could work:

While I like having these classes declared together, there are a few things that need cleaning up. Firstly, we need to pass in the driver. In order for that to happen, we need to create our driver class here too. You can pass the Wikipedia URL to the constructor and pass the driver to the WikipediaPage object:

So close! We need to cleanup SeleniumFunctions and give it a constructor that takes in the driver, right? Lets pop on over and see what the class looks like without the OpenBrowser function in it:

Well, besides the WikipediaPage object that we aren’t using anymore, it seems mostly okay. Because we have kept our classes to single responsibilities, the refactoring we did elsewhere didn’t affect this code. We can create our constructor that sets the WebDriver:

Add it to the WikipediaTests declaration:

And then start to clean up our tests:

Since the driver constructor navigates us to the Wikipedia main page, we can remove the OpenBrowser() lines of code. Similarly, since the page object is no longer a part of the functions object, we can remove the functions. prefixes. Let’s try running the tests:

All green checks!

It seems to me like we’ve setup a nice little framework here. We understand where to place different types of code and why to place it there. Let’s take another top-level look at our framework:

For clarity, objects are green and functions are blue.

Interestingly, we see that every file has an instance of WebDriver in it. Are all of those necessary?

Encapsulating Redundency

It might start to feel like we’re going around a wheel of designs and never landing on one. You might start to feel betrayed. Keep in mind that we’re following an intuitive process – we try a few things out to discover what works and what doesn’t. Let’s explore a little bit more; try commenting out (using //) the driver in SeleniumFunctions.java to see what happens.

You’ll notice a pattern where each of the broken functions are calling findElement, except for the ValidateURL function. Going back to our guiding principle – what is the purpose of this class? To provide actions for Wikipedia automation. That ValidateURL function seems like something that has to do with the browser, instead of Wikipedia. Lets move it to the DriverManager.java class. Since the driver already exists there, it should paste right in without any problems. On the test side, the function now belongs to driverManager instead of functions.

With that out of the way, our errors all relate to using the findElement function. If the responsibility of this class is to do actions, where can we do this finding? I think the “bookmark” class that we setup earlier, WikipediaPage, can hold onto these. Lets move over the searchbar and searchbutton WebElements from the SearchWikipedia function to WikipediaPage.java. Just like with the RadioButtons, they’ll need to become public and return a WebElement:

Then, in order to call something from the WikipediaPage, we’ll need to add it to the class and into the constructor.

Wait a minute, didn’t we just remove that?

You’re right! In the pattern where everything had access to the WebDriver, we didn’t need to keep a reference to the WikipediaPage. Now that it’s been moved around, we need it back. Don’t be afraid to shuffle things around while you look for patterns to emerge, and use comments liberally to quickly shift between patterns of code.

We can update the SearchWikipedia function to call the methods from the WikipediaPage:

Just when you thought things couldn’t get any simpler!

Lets follow this pattern for the ClickMenu function as well. We can move over the hamburger button locator and the menu items locator to WikipediaPage.java. Once we update the locators, we have a pretty clean function:

It’s obvious at a glance what this function is doing.

Since we encapsulated the element finding to the WikipediaPage class, the reduced noise makes this code a lot easier to read. Even if we didn’t know what each of these functions does, we can intuit the goal of the function from the logic and the naming conventions.

I’ll take another break here to encourage you to run the tests again and make sure that they work (they should). When doing these kinds of large scale refactors, don’t let yourself get too far off track without checking in. The setup at the top of tests should change slightly:

The order will matter!

Our WikipediaPage.java has grown and changed recently, lets take another top-level look at it and see if anything stands out to us:

The red boxes help make things stand out

Weren’t we trying to encapsulate the driver? Would it be possible to avoid passing it into the RadioButton objects? Let’s start by commenting out the driver from RadioButton.java:

Same culprit.

Following our intuition, we can identify that the only reason that we use the driver is to find the root element that we search from. Instead of doing the finding inside the class, we can find it inside WikipediaPage and pass that WebElement to the RadioButton to work with. That’s what WikipediaPage’s responsibility was, right?

I left the last one unchanged to highlight the difference

Back in the RadioButton class, we can change the constructor to require the elementContext as a WebElement. Since the driver and By are doing the heavy lifting on the WikipediaPage class, we don’t need them here anymore. With that done, we can remove references to the driver.

That wasn’t too hard, was it? Besides the complicated paradigm shift from a logical standpoint, making the change was just moving around a few object types. Now that we’ve removed the driver references from the both SeleniumFunctions and RadioButton, let’s take another look at our project structure:

Although the size of the page grew, the responsibility of each class has emerged.

I think that with this architecture we’ve landed our little framework in a place that makes sense. We know what each class’s responsibility is and we’ve removed sources of overlap. Is there more we could do? Probably. Is there more we should do? We’ll let the code decide. When we notice code not scaling efficiently or errors start to pop up, it’s time to hit pause and consider refactoring just like we did here.


Let’s explore some hypotheticals and imagine how things might look if we added more code.

Scaling Into More Code

We know Wikipedia. We love Wikipedia. Maybe not enough to donate, but we thank them for their service. We’ve only begun to scratch the surface of what Wikipedia can do. What could it look like if we added more functions to our framework?

We haven’t done much with the actual article view – maybe if we mapped out the table of contents and added a function for getting the text of a section would be a good start. Articles have an “article” and a “talk” view, lets map that out too.

I left out the tests class to save space

The WikipediaPage is starting to get a little large – what would it look like to break it into two different classes?

I called the sections around the article the “header page” to keep it simple.

The functions class will need to start incorporating each page into itself and the pages will all need an instance of WebDriver. This is fine, but as this scales up into a dozen pages, we start to see WebDriver getting used in multiple places. That’s not necessarily a bad thing, either! If you abstract something to the point where it is no longer intuitive, you’ve just traded one problem for another.

What you could do, however, is try implementing a base class. This lets you extend variables and functions to any class that inherits it. If we wanted to move the driver and the findElement functions to this base class, we wouldn’t need to call them in future classes.

Does this offer a lot of value? Not especially. If there were shared elements on both pages it would make sense, but in this situation it doesn’t offer us much of an advantage.

Wikipedia has had it’s moment. What if we wanted to take this framework and move it into the next level? We could open up a browser to to a search engine (Google) and navigate to Wikipedia from there. How could a second website fit into our architecture?

Unlike with the Pages, the Functions classes don’t seem to have much overlap. Its’s still good to break these up into separate classes, but unless the pages start to share the same functions there’s no reason to break them up.

It’s a lesson of could versus should. It’s fun (at least to me) to follow my intuition and break up things into atomic pieces. Is there any value in doing that? Sometimes not. If you recall the from the start of this series, we had a single script that did a basic search. Everything was hard-coded and none of the pieces were reusable. Now, at the end, we have an engine whose parts can create tests quickly and continue to be extended. This is (more or less) a pattern known as the page-object model. It’s a great start for automation and it doesn’t rely on anything too advanced.

So why did we do all of that refactoring?

Hopefully you’ve learned something about engineering. Sure, getting setup with Selenium and Java is nice, but you probably could have run for a long time with that first script. Imagine if you did write a few scripts and then you needed to refactor it all like we did here. It’d take a heck of a lot longer.

If you follow your intuition and allow the pattern to present itself to you, you’ll avoid wasting time under or over-engineering your future code.


What’s next? Pretty much anything on the ‘ol worldwide web. Find a website that you like and start to mess around. Anything that you find yourself doing manually and consistently is a great candidate for automation. Not that I would recommend paying your bills with a script – I’m just saying its possible.

I have to admit – you probably don’t want to have to declare each page object like we have here. You should look into a factory method pattern to scale this code up. There are lots of resources available online for Java, Selenium, and web automation and I’d recommend doing more research into it if you enjoyed this series.

If you didn’t enjoy this or had a hard time following along with the code, that’s okay too! A different tutorial might speak to you better than mine does. Perhaps you don’t like coding? Hopefully you learned something and picked up a few nuggets of wisdom along the way. That’s what it’s really all about.



Leave a Reply

Your email address will not be published. Required fields are marked *