...
When you first access the Hurricane service you will see the list of previously created test definitions.
...
Let’s create a new test:
Enter a name for your test in the Name field at the bottom of the page
Click the “Add Test Definition” button
Your test is created using the name you provided and you are redirected to the Edit Test Definition page.
...
Code Block | ||
---|---|---|
| ||
{
"headers": [],
"steps":
[
{
"name": "",
"type": "Request",
"method": "GET",
"url": "",
"headers": [],
"body": null,
"actions":
[
]
}
]
} |
...
Code Block | ||
---|---|---|
| ||
{
"headers": [],
"steps":
[
{
"name": "Hello World",
"type": "Request",
"method": "GET",
"url": "http://localhost/hello",
"headers": [],
"body": null,
"actions":
[
]
}
]
} |
...
Let’s add a property to our test that will hold the protocol and server information for our API request. To add a test property:
Click on the “Add” button in the Test Properties section of the Edit Test Definition page. This adds an empty property entry to the page
Provide a name for the property. For this tutorial we’ll use “ServerDomain”. The name will be used as part of the placeholder and can only contain letters, numbers, ‘-', and '_’
Optionally provide a default value to use for this property if a specific value isn’t given during test scheduling. For this tutorial we use our current server address: “http://localhost”
Once added, click the “Save” button at the bottom of the page
Reference a Test Property
...
Code Block | ||
---|---|---|
| ||
{
"headers": [],
"steps":
[
{
"name": "Hello World",
"type": "Request",
"method": "GET",
"url": "{{ServerDomain}}/hello",
"body": null,
"actions":
[
]
}
]
} |
...
Code Block | ||
---|---|---|
| ||
{
"headers": [],
"steps":
[
{
"name": "Echo",
"type": "Request",
"method": "POST",
"url": "{{ServerDomain}}/echo",
"headers": [],
"body":
{
"text": "My text content"
},
"actions":
[
]
}
]
} |
...
Code Block | ||
---|---|---|
| ||
{
"headers": [],
"steps":
[
{
"name": "Echo",
"type": "Request",
"method": "POST",
"url": "{{ServerDomain}}/echo",
"headers":
[
{
"name": "Content-Type",
"value": "application/json"
}
],
"body": "{\r\n\"text\": \"My text content\"\r\n}",
"actions":
[
]
}
]
} |
Notice how special characters like line returns and quotes are escaped using ‘\'. If the content contains ‘\’ prior to being escaped, the ‘\’ is escaped by doubling up the '\’ to “\\”. Also, notice that the Content-Type header has to be explicitly added to the header section of this request (or could be added to the top-level header property that is inherited by all steps. Another consideration when deciding to use the short vs long form is the use of properties and variable placeholders. If the placeholder is contained in a quoted property name or value, the short form can be used without issue. However, if the placeholder is expected to be replace with structured content and the use of the placeholder is not quoted, the script definition will no longer be valid Json.
Info |
---|
You do not need to provide the Content-Length header with your request body. The system will automatically calculate the length of the body after any property or variable replacements and add the header for you. |
Making Multiple Requests
A test case may require making multiple requests. The top level “steps” property of the test definition is an array that can contain any number of test steps that will be executed in the order they appear when the test is run. Let’s combine our previous test definitions together:
Code Block | ||
---|---|---|
| ||
{ "headers": [], "steps": [ { "name": "Hello World", "type": "Request", "method": "GET", "url": "{{ServerDomain}}/hello", "body": null, "actions": [ ] }, { "name": "Echo", "type": "Request", "method": "POST", "url": "{{ServerDomain}}/echo", "headers": [ { "name": "Content-Type", "value": "application/json" } ], "body": "{\r\n\"text\": \"My text content\"\r\n}", "actions": [ ] } ] } |
...
Code Block | ||
---|---|---|
| ||
{ "headers": [], "steps": [ { "name": "Hello World", "type": "Request", "method": "GET", "url": "{{ServerDomain}}/hello", "body": null, "actions": [ { "name": "Capture Result", "type": "json", "extractionPairs": [ { "jsonPath": "text", "variableName": "myvar" } ] } ] }, { "name": "Echo", "type": "Request", "method": "POST", "url": "{{ServerDomain}}/echo", "headers": [], ["body": { "nametext": "Content-Type", "value": "application/json" } ], "body": { "text": "My text My text content" }, "actions": [ ] } ] } |
...
Code Block | ||
---|---|---|
| ||
{ "headers": [], "steps": [ { "name": "Hello World", "type": "Request", "method": "GET", "url": "{{ServerDomain}}/hello", "body": null, "actions": [ { "name": "Capture Result", "type": "json", "extractionPairs": [ { "jsonPath": "text", "variableName": "myvar" } ] } ] }, { "name": "Echo", "type": "Request", "method": "POST", "url": "{{ServerDomain}}/echo", "headers": [], ["body": { "nametext": "Content-Type", "value": "application/json" } ], "body": { "text": "[[[[myvar]]" }, "actions": [ ] } ] } |
...
Info |
---|
Information about the differences between Properties and Variables can be found here |
Notice that the format of a variable reference is different from a property in that instead of surrounding the property name with “{{“ and “}}” you use “[[“ and “]]” for variables. When the session runs, it first makes the request to the hello world endpoint and then grabs the value from the “text” property of the response and stores it in the “myvar” variable. When the echo request is made, the placeholder reference “[[myvar]]” is replaced with the value stored in the “myvar” variable; “Hello World!” in our case.
Each time the session starts over, any variables captured during the previous run are removed to ensure nothing is carried over between loops. The only exception to this are variables captured or modified in the Initial and Maintenance scripts. Those variables are shared with all sessions and can only be modified by those scripts. If you attempt to update the value of a global variable within the Main script, and error will be raisedan error will be raised. Once a variable is populated, it is available to all later requests during the same loop even if the request is part of a different group.
Grouping Requests Together
Although the Main script provides the top-level “steps” array that can hold multiple steps, it’s sometimes beneficial to explicitly group steps together. You can provide a name for the group and the system will track metrics of the group as a whole in addition to the metrics from each request.
Sequence Group
The first type of group is a Step Sequence and acts exactly like the top-level “steps” array. The steps defined in the group are executed in the order they appear. Let’s move our existing requests into their own sequence:
Code Block | ||
---|---|---|
| ||
{
"headers": [],
"steps":
[
{
"name": "Hello and Echo",
"type": "Sequence",
"headers": [],
"steps":
[
{
"name": "Hello World",
"type": "Request",
"method": "GET",
"url": "{{ServerDomain}}/hello",
"body": null,
"actions":
[
{
"name": "Capture Result",
"type": "json",
"extractionPairs":
[
{
"jsonPath": "text",
"variableName": "myvar"
}
]
}
]
},
{
"name": "Echo",
"type": "Request",
"method": "POST",
"url": "{{ServerDomain}}/echo",
"headers": [],
"body":
{
"text": "[[myvar]]"
},
"actions":
[
]
}
]
}
]
} |
The structure of the sequence group is very similar to the top-level script except the addition of:
name - The name of the group
type - The type of the group (Sequence, Random)
The top-level script now has a single step; our sequence group. When the sequence group step is executed, its children are then executed in order before the sequence step is complete. If we were to add another Request to our top-level script after the the sequence, the sequence (and its two children) would be completed prior to making the third request. Being able to run a number of steps in order as a group can help organize the test. But what if you want to add some randomness to the test?
Random Group
Similar to the Sequence group, the Random group step allows you to define a set of child steps. However, instead of running each step in order each time the group is executed the Random group will pick one of the children at random and run that step. Once the selected step is executed, the Random group is complete and the test continues on to the next step at the same level as the Random Group. Let’s look at script that uses a Random group:
Code Block | ||
---|---|---|
| ||
{
"headers": [],
"steps":
[
{
"name": "Pick One",
"type": "Random",
"headers": [],
"steps":
[
{
"name": "Call 1",
"type": "Request",
"method": "GET",
"url": "{{ServerDomain}}/call1",
"body": null,
"actions":
[
]
},
{
"name": "Call 2",
"type": "Request",
"method": "GET",
"url": "{{ServerDomain}}/call2",
"headers": [],
"body": null,
"actions":
[
]
}
]
},
{
"name": "Hello World",
"type": "Request",
"method": "GET",
"url": "{{ServerDomain}}/hello",
"body": null,
"actions":
[
]
}
]
} |
When this test is run, the Random group step is executed first. The group will pick either Call 1 or Call 2 to execute. The test then moves on to execute the Hello World step. During the next loop, the Random group will make another random selection.
Using the Initial Script
The Initial script is used to perform any setup tasks required by the Main script such as authentication or gathering data to use in variables. It is run prior to the Main and Maintenance scripts and unlike the other scripts, the initial script is only executed once. Let’s look at an example that uses the Initial script to perform authentication that will be used by the requests in the Main script:
Code Block | ||
---|---|---|
| ||
{
"headers": [],
"steps":
[
{
"name": "Authenticate",
"type": "Request",
"method": "POST",
"url": "{{ServerDomain}}/login",
"body":
{
"username": "{{username}}",
"password": "{{password}}"
},
"actions":
[
]
}
]
} |
The login endpoint sets a cookie that contains the authentication token. This cookie will be sent as part of any requests made by the Main or Maintenance scripts automatically. Note that cookies set in the Initial and Maintenance scripts are global and shared with all sessions running the Main script. Unlike Main script cookies they will not be cleared between session loops. The Initial script is great for authentication, but what if that authentication will expire before the test has finished? Some tests might take several hours to complete. This is where the Maintenance script comes into play.
Using the Maintenance Script
The Maintenance script and delay are used to perform periodic actions that support the Main script. The platform will wait for the delay period before running the Maintenance script for the first time. Once it it complete, it will be run again after the same delay period. This loop will continue until the Main script has finished. In this example we’ll use the Maintenance script to refresh our authentication cookie every 10 minutes:
Code Block | ||
---|---|---|
| ||
{
"headers": [],
"steps":
[
{
"name": "Refresh Authentication",
"type": "Request",
"method": "GET",
"url": "{{ServerDomain}}/refreshlogin",
"body": null,
"actions":
[
]
}
]
} |
In this case we don’t need to send any additional information because our /refreshlogin endpoint will receive the cookie that was set by the Initial script (it is shared globally) and set an updated cookie value during the response. The updated cookie is also shared globally so any new requests made by the Main script will use the new value.
Info |
---|
The Maintenance Delay field is the amount of milliseconds between Maintenance script runs. |