Reusing Code
There’s no way to get around it: Repetition is a fact of life in testing. Fortunately, there are several easy ways to let scripts and handlers do the repeating for you.
Calling Scripts from Other Scripts
One of the most common ways to reuse code is to call a script from a different script. To call a script, insert the script's name in your code, followed by any parameters that the script requires. The script call can be treated as if the script itself were a command or a function. For detailed information about calling other scripts, see Running Other Scripts and Handlers.
Following are examples of calling scripts from within a script:
CycleWindows // Calls a simple script name as a command
ConnectionScript "Old SUT", "New SUT" // Calls a simple script name with parameters
put CaptureTheScreen("SomePrefix",1) into myScreenCapture
// Calls a script as a function with parameters and puts the result in a variable
Whether you use the command call or the function call depends on the script itself. Both types can take input parameters, in which case the called script needs to begin with a params
declaration. The function call also returns values, in which case the called script must include a return
statement.
If you use a simple script name, with no spaces or special characters, and store scripts in the same suite or a helper suite, you can use the script name directly. See the examples above.
For a script in an unrelated suite, or a script with spaces or special characters in its name, you can type the Run
command, followed by the script path:
Run "/Users/Seiji/Documents/EggPlant Suites/Validation.suite/Scripts/ConnectionScript"
// Calls the Run command to open a script from an unrelated suite
Because of this ability to call a script from within another script, it's useful to think about writing scripts in segments that perform specific tasks or actions. This "modular" approach allows you the most opportunity to reuse scripts in different contexts. In such a case, your "main" script would include code for test or environment specifics but would call out to a different script to complete generic tasks.
More detailed information about using handlers and helper suites, which are both features that can help with code reuse and modular design, can be found below.
Handlers
To reuse code within the same script, you can write a handler
, which is essentially a subscript. A handler is a part of your main script that you can call anytime, as if you were calling another script.
Example: CaptureTheScreen
handler
to CaptureTheScreen prefix, count // Starts the handler
set fileName to prefix & "ScreenShot" & count // Puts prefixScreenShotCount into fileName
set tempFile to "/tmp/" & fileName // Puts "/tmp/filename" into tempFile
CaptureScreen tempFile //Captures a screenshot
return fileName // Sets fileName as the function return value
end CaptureTheScreen //Ends the handler
Returning Results of a Handler
If a script or handler returns a value, like this one, you can access the value by calling the Results
function in the next line. So, using the example above, you can call the handler and access the result as shown below.
Example: Returning results of a handler
CaptureTheScreen "MacTest", 6 // Calls the CaptureTheScreen handler
Set newFile to the result // Puts the returned value into a variable
Handlers as Commands or Functions
Because the CaptureTheScreen
handler is a generic handler, it could also be called as a function. The major difference in use is that a command is a complete statement by itself, and may be used without regard for whether it returns a value, while a function must be called as part of an expression.
Example: Calling handlers as commands and functions
CaptureTheScreen "MacTest", 6 // Called as a command; runs the handler
put CaptureTheScreen ("MacTest",6) // Called as a function; returns the value of the handler. Note parentheses.
Calling a handler from another script
There are three ways to call a handler from another script:
- Call the script name with a possessive
‘s
, followed by the name of the handler and its parameters. - Call the script name, adding a dot and the name of the handler to it.
- Call the handler name, followed by the word
of
and the script name.
Example: Calling a handler from another script
run Logging's CaptureTheScreen "MacTest", 6 // Calls handler with script name and possessive 's
run Logging.CaptureTheScreen "MacTest", 6 // Calls handler in the form script.handler
run CaptureTheScreen of Logging "MacTest", 6 // Calls handler with handler "of" script
Code Refactoring
Sometimes as you're writing scripts, you might find that a section of the script could be useful in other contexts or for other scripts. For instance, you've written a procedure that performs an action on your SUT that you'll need to repeat frequently, such as returning to the desktop. You might need to perform the action at different points in the script, and if you're using the same SUT in different tests, you might be able to use the same procedure in different scripts.
Extract Code to a New Script or Handler
You can extract an existing section of code to create a new script or handler using the Extract Code feature. It allows you to move simple or complex sections of code into new scripts. Aim to make these scripts modular so that they can be reused in many different contexts.
Before You Start
- Extracting Global and Universal Variables: If the code block contains explicit global or universal variables, you can either include or exclude them from your new code. When you're splitting up a larger script into several smaller parts, it makes sense to include them. When you’re creating a reusable script or handler that will be called from multiple places, it is probably a better idea to exclude them. This approach helps to keep the new component as clean, focused and reusable as possible.
- Extracting Other Variables: If the code you select to extract includes variables that are referenced outside that section of code:
-
Any variables that are used in the selection that also
exist earlier
in the script are added as the new script'sparams
declaration. The new script is called as a command, so in the Script Editor, the variable will be listed after the new script name as a parameter when it executes.ExtractedScript MyVar
-
Any variables that are used in the selection that also
exist later
in the script are added as the new script'sreturn
declaration. In this case, the new script is called as a function instead of a command, and the returned value is put into the variable:put ExtractedScript(MyVar) into MyOutVar
-
Extract Your Code
Select the code that you want to reuse:
- In the Script Editor, select or highlight the code you're interested in.
- Either right-click the highlighted code block and select Extract Code..., or from the Edit menu, select Extract Code.... The Extract Code pane is displayed, which allows you to create a new script or handler from the code block.
To create a new script:
-
In the Extract Code pane, select Script.
-
Enter a name for your new script.
ノート- You can’t give the script the same name as an existing one. If you try to, the script name turns red to show that it is invalid.
- The name also turns red if you try to enter any characters that are invalid in a SenseTalk identifier.
-
By default, new scripts are saved to the Scripts folder of the selected suite. If you want to change this, either:
- Select another one of the existing folders or subfolders from the Where list, or the file browser beneath it.
- Click New Folder to add a new location.
-
By default, any global or universal variables in the code block are extracted. Clear the Include globals in extracted code checkbox if you want to keep them out of your new code.
-
Click Extract. In the Script Editor, the code block that you selected is replaced by a single line of code that calls the new script. The new script is also created and added to the Scripts list.
To create a new handler:
-
In the Extract Code pane, select Handler.
-
Enter a name for your new handler.
ノートIf you try to enter any characters that are invalid in a SenseTalk identifier, the handler name turns red until you replace them.
-
By default, any global or universal variables in the code block are extracted. Clear the Include globals in extracted code checkbox if you want to keep them out of your new code.
-
Click Extract. In the Script Editor, the code block that you selected is replaced by a single line of code that calls the new handler. The new handler is also added at the end of the script.
Remember, the newly created script can be called by its name from other scripts in addition to the one from which it was extracted. For detailed information about calling other scripts, see Calling Scripts from Other Scripts above or the Running Other Scripts and Handlers page.
Helper Suites
You can make any of the scripts, images or handlers in a suite available for reuse to scripts in other suites by adding that suite as a "helper suite". An Eggplant Functional script searches for images and other scripts in its own suite first. If you add helper suites, it then searches those suites when it cannot find the specified image or script in its own suite.
To add a helper suite:
- Click the Settings button at the bottom of the Suites window.
- When the Settings tab opens, click Add at the bottom of the Helper Suites pane to add one or more helper suites. A file system browser dialog opens for you to navigate to and select the suites you want to specify as helper suites.
To create a new helper suite, you can use one of two methods.
-
From the File menu, select New Helper Suite OR from the Helper Suites pane, click New.
-
Name the new suite and choose where to save it.
Helper Suites Pane
Suite Location Pathing
Referencing the paths to different suites can be tricky, especially if you are using relative paths, or checking the suites and files in and out of source control. There are few different ways you can handle these paths. One option is to write your script to get the suite location and store it in a variable as shown in the following statements:
Example:
put SuiteInfo().path // Returns the path to suite containing this script
Example:
put the folder of me // Returns the full path to the Scripts folder containing this script
See Using a Relative Path for a Helper Suite to learn more.
Suite Global Properties
As an alternative to using helper suites, Eggplant Functional provides the following global properties to specify suites a script can search before or after it searches its own suite:
the InitialSuites
global property allows you to specify a list containing the suite or suites a script should searchbefore
it searches its own suite.the FinalSuites
global propery allows you to specify a list containing the suite or suites a script can searchafter
it searches its own suite.
Example:
set the InitialSuites to (DSD & "myFirstSuite.suite", DSD & "mySecondSuite.suite")
Example:
set the FinalSuites to (DSD & "mySuite.suite", DSD & "myOtherSuite.suite")
In the code above, DSD
is a variable used to represent the Default Suite Directory. You would have to set the variable to the appropriate path for this directory (or to the location for your specific suites) before running these lines of code.
Cross-Platform Applications
The articles in this section explain how you can build upon your current scripts to expand your testing to multiple operating systems.
Creating Platform- and Language-Specific Suites
At first, it might seem easy to create a whole new set of scripts for every platform you need to test, but keeping those scripts consistent as your application evolves can be a real challenge.
A friendlier long-term solution is to start with a base script, and call an outside script for each mechanic that changes between operating systems. You can write the script differently for each platform-specific suite, and then tell the script which suite to use for any given run.
Example: Quit scripts
The below examples could be saved in a script called Quit
, which could then be called as a command from another script. Functions or a conditional statement could be used to determine which line of code is executed, customizing the execution to the application under test.
For Windows:
TypeText ControlKey & AltKey & "x"
For Mac:
TypeText CommandKey & "q"
Designating the Correct Suite
The InitialSuites
global property tells a script which suite (or suites) to check first when it is looking for script or image resources. (These suites even precede the suite in which the current script is running.) If you name more than one initial suite, remember that each suite’s helpers (and their helpers) are searched before the next "top level" suite.
Example: the InitialSuites
This example ensures that your base script finds the correct Quit
script by setting the InitialSuites
value to the appropriate suite:
Set the InitialSuites to ("Windows") -- Names the Windows suite as the first suite to check for resources
Quit -- Runs the first Quit script found
Like all global properties, the InitialSuites
can be changed at any point during a script. (Initial
refers to the initial suite that is searched, not the initial state of the script.)
Using Multilingual Text
Whenever an interface element can be identified by its text alone, you can use Optical Character Recognition (OCR) rather than a captured image in your script. This can save a lot of time creating multiple language suites.
For example, suppose you have to click the same Open button in an application with English, French, and Spanish versions. You could always capture a separate image of the button for each language, but it would be more efficient to use OCR instead.
Example: Using language-specific OCR searches
Languages can be specified in OCR searches using the Language property. For a list of all supported languages, see OCR Language Support.
Click (text: "Open", Language: English) -- Searches for the text "Open" using the English language dictionary provided with OCR and clicks it.
Click (text: "Ouvrir", Language: French) -- Same as above, but with the French dictionary.
Click (text: "Abierto", Language: Spanish) -- Same as above, but with the Spanish dictionary.
Example:
This example creates a list of possible OCR searches that functions much like an image collection. This code will click the "open" button, whatever language is being used in the application under test at the time (options being English, French, or Spanish):
Put {
(text: "Open", Language: English),
(text: "Ouvrir", Language: French),
(text: "Abierto", Language: Spanish)
} into ButtonTextOptions -- Create a list of possible OCR searches, similar to an image collection
Click ButtonTextOptions -- Click the "open" button, whichever language the text is in
One Step Further—Translation Scripts
You can use your language-specific suites even more efficiently by putting a translation script in each one, as shown below.
Example: Translate script
params EnglishWord -- Takes an English word...
set translation to {Hello: "Bonjour",Open: "Ouvrir",Yes: "Oui"} -- Sets the translation variable to a property list, in which each English key is assigned a French value
return translation's (EnglishWord) -- Returns the French value of the English key that was passed in
Example: Calling the translate script
Set the InitialSuites to ("French") -- Ensures that the "Translate" script comes from the French suite
Click (text: Translate ("Hello")) -- Runs the "Translate" script with "Hello" as the EnglishWord parameter. Uses the returned value of "Translate" as the Text value in this property list.
When you are using a translation script, don’t forget to create a version that works for your base language! Even though you don’t really need to have your own language translated back to you, your base scripts do need something to do when they encounter the Translate
call. The base-to-base "Translate" script does not need to go through the motions of translating anything; just taking a parameter and returning the same parameter is enough.
Example: Translate script
params BaseWord// Takes a word...
return BaseWord// Returns the same word.
Testing Across Desktop and Mobile Devices
This example uses a single master script to call four sub-scripts that test against the Evernote application on an Android device, as well as through a browser on a desktop machine and through the desktop version of the application.
For complete information about using parameters, see Parameters and Results.