(This article is from infoq: http://www.infoq.com/cn/articles/refactoring-test-scripts)
Introduction
As we all know, automatic test scripts are difficult to maintain. With the wide application of Agile Methodology in enterprise software projects, automated functional testing, one of its core practices, has proved its value while also challenging the project. Traditional "recording-Play" testing tools may help testers quickly create a series of test scripts, but these test codes are difficult to maintain at the end. The reason is that the application is constantly changing.
In the world of programming, "refactoring" (a way to improve the internal structure of software without affecting external behaviors of software) has become a frequently used word among programmers. In short, through refactoring, programmers make the code easier to understand and more flexible in design. Experienced Agile Project Managers allocate a certain amount of time to programmers to refactor code, or use refactoring as part of the completion of user stories. Most integrated development environments (IDES) provide built-in support for multiple refactoring methods.
Testers who develop or maintain automated test scripts are not comfortable with this, although they also have requirements to make the automated test scripts readable and maintainable. New versions of software are accompanied by new features, bug fixes, and software changes. It is difficult to track the corresponding test scripts (and the more test scripts, the more difficult the job is)
Test Reconstruction
The reconstruction goal and process for functional testing are the same as code refactoring, but they have their own characteristics:
- Target Audience
The end users of the testing tool include testers, business analysts, and even customers. The fact is that testers, business analysts, and customers generally do not master programming skills, and the entire paradigm changes accordingly.
- Script syntax
Code refactoring is mainly supported in compiled languages (such as Java and C. Functional test scripts may be XML, vendor-specific scripts, compiled languages, or scripting languages (such as Ruby ). Depending on the test framework, the use of refactoring is also different.
- Dedicated function test refactoring
Many common code refactoring techniques, such as "RENAME", can be used in functional test scripts. They are specific to test intentions, such as "Move the scripts to run each test case ".
Itest2 ide
Itest2 IDE is a new functional testing tool designed for testers to easily develop and maintain automated testing scripts. Itest2 is fully committed to Web testing automation. The testing framework supported by itest2 is rwebunit, which uses the rspec syntax (an open-source plug-in for watir ).
The philosophy behind itest2 is: easy and simple. Trial shows that testers without programming experience can write their first automated test script in less than 10 minutes on average. With itest2, testers can develop, maintain, and verify test scripts for functional requirements. developers can verify that features are available. business analysts/customers can view test results (in a real browser, for example, ie or Firefox.
Test scripts created by itest2 can be run from the command line or integrated into the continuous build server.
Drill
It is better than words. Next, let's take a look at how to use the refactoring tool provided by itest2 to create two test cases to make them easier to understand and maintain.
Test Plan
To practice, we have developed some typical but simple web test scripts for the mecury's newtour website.
Site URL |
Http://newtours.demoaut.com |
Test data: |
User Logon:Agileway/agileway |
Test Case 001: |
A registered customer can choose one-way sailing from New York to Sydney. |
Test Case 002: |
A registered customer can choose the round-trip mode from New York to Sydney. |
Automated Testing |
|
Test script framework: |
Rwebunit (open-source watir extension) |
Test execution method: |
Use the command line or itest2 ide |
Test Editor/tool: |
Itest2 ide |
Create test case 0011. Create a project
First, create an itest2 project and specify the website URL. A simple test script file is created as follows:
load File.dirname(__FILE__) + '/test_helper.rb'test_suite "TODO" do
include TestHelper
before(:all) do
open_browser "http://newtours.demoaut.com"
end test "your test case name" do # add your test scripts here endend
2. Use itest2recorder to record the test script for test case 001
We use itest2recorder, a Firefox plug-in that records user operations in the Firefox browser and records executable test scripts.
enter_text("userName", "agileway")enter_text("password", "agileway")click_button_with_image("btn_signin.gif")click_radio_option("tripType", "oneway")select_option("fromPort", "New York")select_option("toPort", "Sydney")click_button_with_image("continue.gif")assert_text_present("New York to Sydney")
3. paste the recorded test script into a test script file and run
# ...
test "[001] one way trip" do enter_text("userName", "agileway") enter_text("password", "agileway") click_button_with_image("btn_signin.gif") click_radio_option("tripType", "oneway")
select_option("fromPort", "New York") select_option("toPort", "Sydney") click_button_with_image("continue.gif") assert_text_present("New York to Sydney")end
Run the test case now (right-click and select"Run [001] one way trip
), It passed!
Use the Page Object for refactoring
The above test script can work, and the rwebunit syntax is also very easy to read. Some people may question the requirements for refactoring, and may also ask what is going on with page?
First, the test script is not easy to maintain in the current format. Assume that we already have hundreds of automatic test scripts, and the new software has modified the user authentication method and used the customer's email address to log on as the user name, this means that we need to use 'email 'in the test script instead of 'username '. Searching for replacement in hundreds of files is not a good idea. Besides, project members also like to use the general terms in the project. There is a wonderful name for them: domain-specific language (DSL ). It's amazing to use these words in the test script.
This can be done well using the Page Object. A page object represents a logical web page that contains the operations that end users can perform on the page. For example, in our example, the home page contains three operations: "enter user name", "Enter Password", and "click Login ". "Using Page Object for refactoring" refers to the process of extracting operations to specific page objects, and itest2 provides support for such refactoring, which you can easily achieve.
1. Extract to the homepage object
The logon function occurs on the home page. We will hand this over to the homepage. User logon is a common function. We use three lines of statements (enter the user name, password, and click the logon button) to complete this operation. Select the three lines of code and click "extract page..." under the "refactoring" menu (the shortcut key is CTRL + ALT + G ).
Figure 1. "refactor" menu -- "extract page"
As shown in, a window pops up asking you to enter the name and function name of the page object. Here, we enter "Homepage" and "login" respectively ".
Figure 2. "extract page" dialog box
The selected three lines of code are replaced:
home_page = expect_page HomePagehome_page.login
This will automatically create a new file "pages/home_page.rb" with the following content:
Class homepage <rwebunit: abstractwebpage
Def initialize (browser)
Super (browser, "") # todo: Add identity text (in quotes)
End
Def Login
Enter_text ("username", "agileway ")
Enter_text ("password", "agileway ")
Click_button_with_image ("btn_signin.gif ")
End
End
Run the test case again and it should still pass.
Note:: As Martin Fowler points out, the pace of refactoring: Testing, small changes, testing, and small changes. This pattern ensures the rapid and secure reconstruction.
2. selectflightpage Extraction
After successful login, the customer enters the flight selection page. Unlike the logon page, each operation may be modified by Different developers, So we extract each operation as a function. Move the cursor to this line
click_radio_option("tripType", "oneway")
Execute"Extract to Page...
"Refactoring command (Ctrl+Alt+G
), Enter"SelectFlightPage
Andselect_trip_oneway
".
select_flight_page = expect_page SelectFlightPageselect_flight_page.select_trip_oneway
3. Continue to extract more operations to the selectflightpage object.
Continue to reconstruct the functions on "selectflightpage": "select_from_new_york", "select_to_sysydney", and "click_continue ".
test "[1] one way trip" do home_page = expect_page HomePage home_page.login select_flight_page = expect_page SelectFlightPage select_flight_page.select_trip_oneway select_flight_page.select_from_new_york select_flight_page.select_to_sydney select_flight_page.click_continue assert_text_present("New York to Sydney")end
As usual, we run the test case again.
Compile test case 002
After refactoring test case 001, we now have two page objects ("Homepage" and "selectflightpage"), so it is much easier to compile test case 002 (by reusing them ).
1. Use existing Homepage
Itest2 ide provides built-in support for page objects. Enter "EP" and then press "tab" tabulation key (called "snippets "), the page is automatically filled with "expect_page" and all known page objects are displayed for selection.
Figure 3. Automatically complete the Page Object
We can get
expect_page HomePage
To use homepage, we need to hold its handle (also known as 'variable' in the programming world '). Execute the "introduce page variable" refactoring action (CTRL + ALT + V) to create a new variable.
Figure 4. "refactor" menu-"introduce page variable" menu item
home_page = expect_page HomePage
Enter "home_page." In the new line. The function defined in this page object is automatically displayed for you to choose from.
Figure 5. Page Object Function search
2. Add the method required for Test Case 2
Test Case 002 is similar to test case 001. The difference lies only in the selection and assertions of travel types. With recorder, we can define a new function:
click_radio_option("tripType", "roundtrip")
Reconstructs a new feature of selectflightpage.
select_flight_page.select_trip_round
It becomes
test "[2] round trip" do home_page = expect_page HomePage home_page.login select_flight_page = expect_page SelectFlightPage select_flight_page.select_trip_round select_flight_page.select_from_new_york select_flight_page.select_to_sydney select_flight_page.click_continue assert_text_present("New York to Sydney") assert_text_present("Sydney to New York")end
Run the test script of Test Case 2 (right-click on any row of Test Case 2 and select "Run..."). The test is successful!
Restore an application to its original state
But wait, we are not finished yet. Test Case 1 is passed, and test case 2 is also passed. But when we run them together, Test Case 2 fails. Why?
We did not restore the web application back to the initial state. After the test case 001 is run, the user still remains logged on. To ensure that the test is independent from each other, we need to ensure that each running test starts with a login and ends with an exit, beginning and end.
test "[001] one way trip" do home_page = expect_page HomePage home_page.login # . . .
click_link("SIGN-OFF") goto_page("/")end
test "[002] round trip" do home_page = expect_page HomePage home_page.login # . . . click_link("SIGN-OFF") goto_page("/")end
Delete duplicate code
The test script already exists. The rspec framework allows you to perform certain operations before or after each test case is run.
Select the first two lines (login function) and press SHIFT + F7 to execute the "Move code" reconstruction.
Figure 6. Reconstruct the menu "Move code"
Select "2 move to before (: Each)" and move this operation
before(:each) do home_page = expect_page HomePage home_page.loginend
As shown in the name, the two steps are executed before each test case is run, so the first two rows in test case 002 are unnecessary. We can also perform similar refactoring to complete the "After (: Each)" section.
After (: Each) DoClick_link ("sign-off ")
Goto_page ("/")
End
Final Version
The following is a complete (fully restructured) test script for test cases 001 and 002.
load File.dirname(__FILE__) + '/test_helper.rb' test_suite "Complete Test Script" do include TestHelper before(:all) do open_browser "http://newtours.demoaut.com" end before(:each) do home_page = expect_page HomePage home_page.login end
after(:each) do click_link("SIGN-OFF") goto_page("/") end
test "[001] one way trip" do select_flight_page = expect_page SelectFlightPage select_flight_page.select_trip_oneway select_flight_page.select_from_new_york select_flight_page.select_to_sydney select_flight_page.click_continue assert_text_present("New York to Sydney") end test "[002] round trip" do select_flight_page = expect_page SelectFlightPage select_flight_page.select_trip_round select_flight_page.select_from_new_york select_flight_page.select_to_sydney select_flight_page.click_continue assert_text_present("New York to Sydney") assert_text_present("Sydney to New York") end end
Adapt to changes
Our world is not perfect. In the software development industry, things frequently change. Fortunately, the above work makes the test script not only easier to read, but also easier to adapt to changes.
1. The customer modified the term
As we all know, using the same language for a project is a good practice, even in a test script. For example, the customer is more inclined to use the term "return trip" instead of "Round Trip ". With the help of refactoring test scripts, this is easy to do.
Move the cursor to the "select_trip_round" function of the "selectflightpage" Class (pages/select_flight_page.rb), and select the "RENAME..." item (SHIFT + F6) under the "refactoring" menu)
Figure 7. "refactor" menu-"RENAME"
Enter the new function name "select_return_trip ".
Figure 8. "RENAME function" dialog box
Other places in the test script that reference "select_trip_round" are changed
select_flight_page.select_return_trip
2. Application Modification
Modifications to applications (from programmers) are more common. For example, if a programmer modifies the flight selection page for some reason
<select name="fromPort">
Change
<select name="departurePort">
Although the user does not notice any changes, the test script (any test case accessing this page) will now fail. If you directly use the recorded script file as the test script, the modification operation will be very tedious and easy to introduce errors.
Go to the "select_from_new_york" method of "selectflightpage" (use the shortcut key Ctrl + T to select"Select_flight_page", Enter the shortcut key Ctrl + F12 and select"Select_from_xx"), Change" fromport "to" departureport ".
def select_from_new_yorkselect_option("departurePort", "New York") # from 'fromPort'end
It looks pretty good!
Conclusion
This article describes how to use the Page Object in automated function testing to make the test script easy to understand and maintain. An actual example of using itest2 ide To improve the test script process demonstrates its rich refactoring functions.
References
Fowler, Martin, et al. refactoring: improving the design of existing code, reading, Mass.: Addison-Wesley, 1999
Certificate ----------------------------------------------------------------------------------------------------------------------------------------------------
Source: AuthorZhimin ZhanTranslatorJin Ming