Creating a Functional Test Framework
To be consistent with my “what I’ve been up to” style of blogging I wanted to talk about the benefits of creating a functional test framework and what that framework might look like.
If you’re like me then you probably think that automated functional testing using Selenium is really cool. If you aren’t like me, but automated functional testing is a necessary evil in your position, then simplifying your interaction with the entire experience should be really cool. In either case, creating, employing, or utilizing a framework for your functional tests can drastically improve your testing in a way that benefits the entire team.
The Order to Things
A member of our team explains functional testing like this; instructions sent to a man who lives in our server, who carries out those instructions on his computer in the exact manner in which they were sent to him. This really isn’t that far off. One of my largest pain points in writing functional tests was that sometimes I would ask this unnamed guy masquerading as computer hardware to click on an element I expected to be on page, but forgot to tell him to wait for it to show up. In fact, I found that there were usually always a certain set of steps and protocols that I should always follow when performing the simplest of actions.
For a click, I need to wait until my element exist on the page, then I need to move my mouse to that element, I need to wait for it to be in a clickable state, and I need to perform the click itself. While I can easily handle this in the test itself, it probably isn’t the easiest place to manage it. Instead, I can setup this sequence as a method in a framework, and call to it in the test. This works well for a couple reasons. The first being that in my test I only need to write one line of code instead of 4. Secondly, with this action handled in the framework I never accidentally forget a step. By doing this I am making my test easier to write, easier to read and I am ensuring consistency across the scope of potential actions.
public static void Click(IWebDriver wd, By by) { try { WaitActions.WaitUntilElementExist(wd, by); var element = wd.FindElement(by); FindActions.MoveToElement(wd, element); WaitActions.WaitUntilElementClickable(wd, element); ClickElement(element); } catch (Exception ex) { throw ex; } }
Don’t Make Me Repeat Myself
This idea of repeatable code extends beyond the steps to perform basic actions in tests. In our functional test infancy we handled most things at the test level. The framework aims to handle most of that work upfront, so the focus can shift to writing the test instructions themselves. The framework handles things like setting up the driver or Eyes for Applitools, in addition to addressing the different actions we can take in our tests.
Another measure along those lines is taking sections of the application that are tested repeatedly and moving them out of the individual tests so that they can be called upon in multiple tests when needed. When we need to update any of these sections, we can do it in one place. If a header on the site changes, we do not have to update every test that interacts with the header, but rather reference the portion of the project that handles interacting with the header.
public void CheckHeader(IWebDriver wd) { var header = FindActions.FindVisibleElement(driver, By.ClassName("header-inner")); var logo = FindActions.FindVisibleElement(driver, By.CssSelector(".logo-inner img")); var topNavLinks = FindActions.FindVisibleElements(driver, By.CssSelector(".mobile-nav .dropdown")); var secondLevelLinks = FindActions.FindElements(driver, By.CssSelector(".dropdown-menu li")); }
Batteries Included
Another cool feature of this framework is that it was made into a NuGet package that can be included in any projects at our company. If I want to create a new functional test solution to go with my existing webapp, I can simply pull in the package and hit the ground running. The NuGet package has all the references it needs already included. The setup of my driver and other essential classes have already been handled in the framework. I can immediately start writing tests. If I focus on the repeatable pieces of my application and create those methods in my best test, I can gain a good bit of test coverage on my application with relatively little overhead.
Simplicity for Adoption
What I hope proves to be the largest benefit of creating the framework is promoting adoption throughout the team because of its simplicity. Team members who were unfamiliar with testing, or more specifically, testing with Selenium and the nuance associated with that, will be able to test simply and reliably with little overhead. Before, we were asking a test developer to understand the finer points of Selenium and how it interacts with the browser to create effective tests. Now, creating a test should translate much more simply into what interacting with the application feels like in real world terms – find this element, click this button, type into this search bar.
Simplifying how testing is done and lowering the barrier to entry on who can create tests should allow for more testing. Troubleshooting and understanding tests should be much easier for developers that interact with the test after they have been created or product owners trying to understand what kind of coverage they have on the application.
Do Less. Get More.
A functional test framework will allow your company to do less work and get more from it. The framework ensures that tests follow the required protocols with more consistency and less overhead. The framework reduces the amount of work and shortens the time required to get a test project up and running. Finally, simplicity in testing can promote adoption throughout the team, leading to more team members creating tests and ultimately more testing as a result. Having a framework in place is essential to any functional test program.