Functional Test Refactoring: Extract Function
Extract a set of test steps performing a common user operation to a reusable function
This is the 1st of 6 Functional Test Refactorings: Extract Function. I will show each refactoring in the following sections:
Motivation, Why?
Mechanics, How?
Refactoring Steps, step by step in TestWise IDE
Sample test scripts (before and after)
Demonstration (animated GIF, easier to follow the core steps)
Demonstration (video, easier to get a flow of actions)
Benefits
Exercises
You may start with any section.
I strongly recommend you do this refactoring exercise (the one demonstrated and the extras). Once you have mastered the concept, performing refactorings is quick, usually in seconds. To help you get into action quickly, I prepared the test project on Github (available for free).
> cd my-working-dir
> git clone https://github.com/testwisely/agiletravel-ui-tests
The test project is at my-working-dir/agiletravel-ui-tests/pre-refactoring
.
While the demonstration is shown in TestWise IDE (created by me), you may use TestWise (in free mode) to do the exercise or use your own preferred tool.
Tip: if you make mistakes and are unable to figure out what to do, reset the test project using Git.
> cd my-working-dir
> git checkout -f
Motivation (Extract Function)
You have long logically grouped test steps to perform a common operation.
For well-understood operations such as user login and credit card payment (formed with a set of individual test steps), it is better to replace them with a reusable function.
Sample Test (before)
The test script file in the sample project: spec/login_spec.rb
it "User can sign in OK" do
driver.find_element(:id, "username").send_keys("agileway")
driver.find_element(:id, "password").send_keys("testwise")
driver.find_element(:xpath,"//input[@value='Sign in']").click
# ...
end
Issues with the test
The three test steps (enter
username
, enterpassword
, click ‘Sign in’) will be used very frequently in many test scripts. Copy-n-Paste is not only inefficient, but more importantly, it is also hard to maintain. Imagine changing the test user name, the ‘Sign in’ button, …, etc. If that happens, you need to update many files.Not very readable for this most common user operation.
Action
Extract these test steps in a reusable function.
Scope
The test script file.
Mechanics
Identify test steps that are reusable for a common operation, e.g., login
Select the test steps
Extract them into a function, give a meaningful name
Replace the original steps with a call to the function
Run all the test cases that have been changed
Refactoring Steps in TestWise
Select the test steps
Invoke the refactoring
Enter a function name
Adjust parameters if necessary (TestWise will analyze the test steps and pre-determine the parameters)
Preview the new function
Apply the refactoring
Extract Function Dialog
A dialog window appears after invoking the ‘Extract Function’ refactoring (with selected test steps). The test engineer may
enter the function name (as the example shown on the right)
edit (add or remove or change) parameters for the function
edit the previewed function directly
Click the “OK” button to apply the refactoring
Expected Result
A new function with the supplied name is inserted in the test scripts, containing the selected test steps
The test steps in the test case are replaced with a call to the new function
Sample Test (after)
def login(username, password)
driver.find_element(:id, "username").send_keys(username)
driver.find_element(:id, "password").send_keys(password)
driver.find_element(:xpath,"//input[@value='Sign in']").click
end
it "User can sign in OK" do
login("agileway", "testwise")
# ...
end
Demonstration (animated GIF)
Invoke Refactoring via the menu
Invoke Refactoring via the keyboard shortcut
As we need to perform refactorings frequently, I suggest using keyboard shortcuts for efficiency (Ctrl + Alt + M on Windows).
Demonstration (Video)
Extract
login
function (multiple steps) via the menu (19s)Extract
logoff
function (single step) via keyboard shortcut (6s)
Benefits
Concise and Reusable
DSL (Domain Specific Language)
You can define your own function name that is meaningful to your team, such aslogin, login_as, sign_in
Reduce Duplication, DRY
In the case of our example, if there are changes to the login page, you will only need to update the function definition once, regardless of how many tests you have.
Exercises
Perform ‘Extract Function’ refactoring to create
login
function (or using your preferred name) in login_spec.rb (as shown)Use the new
login
function in the second test casePerform another ‘Extract Function’ refactoring to create
logoff
function
Related Refactorings
Extract to Page Function
This article is an excerpt from my book “Practical Web Test Automation”.