Skip to main content
Version: 23.5

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:

  1. Call the script name with a possessive ‘s, followed by the name of the handler and its parameters.
  2. Call the script name, adding a dot and the name of the handler to it.
  3. 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's params 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's return 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:

  1. In the Script Editor, select or highlight the code you're interested in.
  2. 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:

  1. In the Extract Code pane, select Script.

  2. Enter a name for your new script.

    note
    • 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.
  3. 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.
  4. 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.

  5. 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:

  1. In the Extract Code pane, select Handler.

  2. Enter a name for your new handler.

    note

    If you try to enter any characters that are invalid in a SenseTalk identifier, the handler name turns red until you replace them.

  3. 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.

  4. 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:

  1. Click the Settings button at the bottom of the Suites window.
  2. 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.

  1. From the File menu, select New Helper Suite OR from the Helper Suites pane, click New.

  2. Name the new suite and choose where to save it.

    Helper suites addition panel 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 search before it searches its own suite.
  • the FinalSuites global propery allows you to specify a list containing the suite or suites a script can search after 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
note

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.

Main Script (EvernoteTest.script)

//This script makes a note in Evernote on an Android device and then checks that the note was processed and shows up in a web browser on a Windows 7 VM//
//Populate variables (could also be drawn from a data file or database)
Put "Bananas" into MyFruit
Put "Carrots" into MyVeggie
AddItemsFromAndroid MyFruit, MyVeggie --Call script to add items to a grocery list from an Android device
CheckItemsInBrowser MyFruit, MyVeggie --Call script to connect to a Win 7 VM and login to Evernote online to see if the items updated
CheckItemsInDesktopApp MyFruit, MyVeggie --Call script that uses the Evernote desktop application to verify the list items
ClearList MyFruit, MyVeggie --Clear the List for Next Test Run

First Called Script (AddItemsFromAndroid.script)

//This script goes into Evernote on Android and adds items to the grocery list
Params Fruit, Veggie
Log "Entering Items in List via Android"
Connect GalaxyS3 //Connect to the device
Tap "AppMenu" //Navigate to the Evernote app
Tap "Evernote" //Navigate to the list
Tap "NoteIcon"
Tap "GroceryList"//Edit the list and add the items
Tap "EditIcon"
WaitFor 30.0, "Check"
TypeText Fruit, return
TypeText Veggie, return
//Save the list and navigate back to the home page
Tap "Check"
Wait 1
Tap "ElephantIcon"
TypeText HomeKey //Navigates to the home page using the home button on the device
WaitFor 30.0, "ChromeIcon" //Verifies that the home page of the device is displayed

Second Called Script (CheckItemsInBrowser.script)

//This script connects to a Win 7 VM and verifies the list items in Evernote through a web browser
Params Fruit, Veggie
Log "Checking the list items in a browser"
Connect Win7Demo //Connect to Win 7 VM
//Navigate to Evernote.com
DoubleClick "GoogleChrome"
WaitFor 30.0, "GoogleMenuLogo"
TypeText "Evernote.com",return
//Log In and make sure grocery list is selected
Click "SignIn"
TypeText "<email address>"
TypeText TabKey
TypeText "<password>"
Click "SignInButton"
Click "GroceryList"
//Find the Date Created and Log it
Put ReadText ("CreatedTLDate","CreatedBRDate") into DateCreated
Log "This List was Created on: " & DateCreated
//Find the Date Last Modified and Log it
Put ReadText ("ModifiedTLDate","ModifiedBRDate") into DateModified
Log "This List was Last Modified on: " & DateModified
//Read the Contents of Grocery List
Put ReadText ("TLList","BRList") into ListContents
Log "The list contained: " & ListContents
//Check to see if Fruit was properly synced
If ListContents contains Fruit
then
log "The list contained " & Fruit
else
logWarning "The item " & Fruit & " was not displayed"
end if
//Check to see if Veggie was properly synced
If ListContents contains Veggie
then
log "The list contained " & Veggie
else
logWarning "The item " & Veggie & " was not displayed"
end if
//Close out of Browser
Click "CloseButton"

Third Called Script (CheckItemsInDesktopApp.script)

//This script connects to a Win 7 VM and verifies the list items in the Evernote desktop application//
Params Fruit, Veggie
//Declares parameters. For an example on passing parameters, please see "Passing Parameters and Calling Functions"
Log "Checking the list items in the Evernote desktop application"
Connect Win7Demo //Connect to Win 7 VM
//Open Evernote desktop application and make sure Grocery List is selected
DoubleClick "EvernoteDesktopApp"
If not imagefound (30.0, "GroceryListSelected")
then
Click "GroceryList"
Click foundImageLocation()
end if
//Sync the list to update any modifications
Click "ToolsMenu"
Click "Sync"
Wait 5
Put ReadText ("CreatedTLDate","CreatedBRDate") into AppDateModified
Log "This List was Created on: " & AppDateModified
//Find the Date Created and Log it
Put ReadText ("CreatedTLDate","CreatedBRDate") into AppDateCreated
Log "This List was Created on: " & AppDateCreated
//Find the Date Last Modified and Log it
Put ReadText ("ModifiedTLDate","ModifiedBRDate") into AppDateModified
Log "This List was Last Modified on: " & AppDateModified
//Read the Contents of Grocery List
Put ReadText ("TLList","BRList") into AppListContents
Log "The list contained: " & AppListContents
//Check to see if Fruit was properly synced
If AppListContents contains Fruit
then
log "The list contained " & Fruit
else
logWarning "The item " & Fruit & " was not displayed"
end if
//Check to see if Veggie was properly synced
If AppListContents contains Veggie
then
log "The list contained " & Veggie
else
logWarning "The item " & Veggie & " was not displayed"
end if
//Close out of Browser
Click "CloseButton"

Last Called Script: Cleanup Script (ClearList.script)

//This script accesses Evernote via Android and clears the contents
Params Fruit, Veggie //Declares parameters
Log "Clearing the list on the Android device"
Connect GalaxyS3 //Connect to the device
//Navigate to the Evernote app
Tap "AppMenu"
Tap "EverNote"
//Navigate to the list and go into edit mode
Tap "NoteIcon"
Tap "GroceryList"
Tap "EditIcon"
WaitFor 30.0, "Check"
//Delete Item 1
TypeText ShiftKey, DownArrow
Click "DeleteButton"
//Delete Item 2
TypeText ShiftKey, DownArrow
Click "DeleteButton"
//Save the list and navigate back to the home page
Tap "Check"
Wait 1
Tap "ElephantIcon"
TypeText HomeKey
WaitFor 30.0, "ChromeIcon"

Running from a Master Script

The Schedules pane of the Suite Window has a convenient graphical interface for organizing and running batches of scripts; however, when you need to run scripts more dynamically, you have to write a master script.

A powerful tool when writing a master script is the RunWithNewResults command, with which you can build upon returned script results to generate a future test run schedule.

RunWithNewResults works by taking another script as a parameter. The parameter script generates its own results, and returns them to the master script. The benefit of this is that you can schedule script runs conditionally, based on the return values of previous executions. You can run any number of scripts through a master script, and manage the results as well.

Example: Master Script

This example script highlights the functionality of master scripts. First, it runs an initial test. If that test fails, it sends an email warning to a system administrator; otherwise it proceeds to execute a series of tests, storing the results in a text string that it logs at the end.

set TestList to ("Test1", "Test2", "Test3") -- Creates a series of script executions to be used later
RunWithNewResults "InitialTest"
set Outcome to the result
if the status of Outcome is not "Success" -- If the result of "InitialTest" is not "Success", gets the date and time of the run...
then
convert Outcome's runDate to date and long time
sendMail (to: "administrator@yourcompany.com", from: "JoeTester@yourcompany.com", subject: "Initial Test Failed", body: "Test run at" && rundate of Outcome && "had" && errors of Outcome && "errors") -- Sends email to report the date and errors of the execution. (&& joins text strings with a space between them.)
else -- Otherwise...
repeat with each testScript of TestList -- For every script in TestList,
RunWithNewResults testScript -- Runs the script, then...
set Outcome to the result -- Puts the results into Outcome
put testScript & ":" && status of Outcome && Return after currentReport -- Adds "Script: Status", then a return character to currentReport
if the status of Outcome is "Failure" then -- If the status property is Failure...
run "CleanupScript" -- Runs CleanupScript to reset the test environment
end if
end repeat -- Ends after the final test in TestList
Log "Final Results:" -- Logs "Final Results:" text
repeat with each line of currentReport
log it -- Logs each line in currentReport
end repeat
end if