The rtv (Runtime Variables) Library
/* @provengo summon rtv */
The Runtime Variable library allows capturing, processing, and testing values during test execution. For example, when testing a search engine, this library can be used to capture the result count of a broad search (say, "Pizza"), the result count of a narrower search (say, "pizza mentions in rock ballads"), and to validate that the broad search had more results than the narrow one.
The supported model API is documented below. Members whose names begin with __, along with the diagnostic toString() method, are internal implementation details.
Other usages include:
-
Using API tokens in tests - receiving one at the beginning of a test and then using it for the rest of the test.
-
Reducing test graph size, by storing data that should not affect behavior on its own. E.g. selecting a town on some initial stage, and then asserting that the system displays the same town at a later stage.
Values are typically stored by methods in actuation libraries such as the Selenium Library's store method.
Using Runtime Variables and the @{} Syntax
Accessing runtime variables is done by enclosing the variable name in @{}. For example, to have an event whose name is tha value of runtime variable rtv1, use Event("@{rtv1}").
Any JavaScript expression can go inside @{}. You can work with multiple variables, and even reference the top-level scope of your model, such as data stored in files at the data/ directory, or functions defined at the lib/ directory.
// ... inside a bthread, bws is a Selenium browser session
bws.store("firstName", XPATH_TO_FIRST_NAME_FIELD);
bws.store("lastName", XPATH_TO_LAST_NAME_FIELD);
bws.assertText(
"Sincerely, @{firstName[0]+'. '+lastName}",
XPATH_TO_PARTING_LINE); (1)
| 1 | Assert that the greeting is in the form of "Sincerely, L. Cohen". |
| It is not possible to access local function variables. The reason is that these will be long gone when runtime arrives - the function runs during scenario generation, whereas test execution (runtime!) is done much later, typically after sampling the model and composing an optimal test suite. |
Example
/* @provengo summon rtv */
bthread("sample-and-check", function(){
// Generate two selenium sessions, s1 and s2.
s1.store("c1", "//*[@id='counter1']"); (1)
s2.store("c2", "//*[@id='counter2']"); (2)
rtv.assertLt("@{c1}", "@{c2}"); (3)
rtv.assertLe("@{c1}", "17"); (4)
rtv.assertEq("250", "@{c2}"); (5)
});
| 1 | Storing the text content of DOM element whose id is counter1 into runtime variable c1. |
| 2 | Same as above, but with counter2 and c2 |
| 3 | Asserting that the numeric value of c1 is less then that of c2 |
| 4 | Asserting that the numeric value of c1 is less then or equal to 17. |
| 5 | Asserting that the numeric value of c2 is equal to 250. |
Events
rtv.storeEvent(varName, value)
Creates an event that stores value in the runtime variable named varName when the event is executed. This method only creates and returns the event; use request(rtv.storeEvent(…)) to add it to a scenario. For a shorthand that creates and requests the event, use rtv.doStore(varName, value).
varName-
String. The name of the runtime variable. The name is evaluated when the event executes; the resulting name must be a valid JavaScript identifier and must not be a reserved JavaScript word.
value-
Any JavaScript value. Strings containing
@{}expressions, and fields of JavaScript objects, are evaluated when the event executes.
Example
bthread("store customer", function(){
request(rtv.storeEvent("customer_num", 42));
request(Event("Validate customer @{customer_num}"));
});
For an example that stores a value produced while a REST request is executing, see the REST library example. Runtime callbacks use pvg.rtv.set(key, value) because the value is available only during test execution; rtv.storeEvent(varName, value) is used when creating the scenario.
rtv.anyStore(varName)
Returns an event set containing all RTV storage events that store values into varName. It is useful for triggering scenarios when a certain variable is updated, blocking a variable from being updated, and defining test-suite goals based on which values were updated.
| This event set does not capture indirect storage events such as methods from the Selenium package that store directly to the Runtime Value Store. |
varName-
String. The name of the runtime variable we want to monitor.
Example
Below, whenever runtime variable customer_id is updated, the on handler prevents any updates to the variable until it completes a set of validations.
// @provengo summon rtv
on( rtv.anyStore("customer_num"), function(){
block( rtv.anyStore("customer_num"), function(){
request(Event("Validate Customer @{customer_num}"));
request(Event("Validate customer count"));
});
});
rtv.assertEqEvent(v1, v2, errorMessage)
Creates and returns an event that compares the string representations of v1 and v2. The assertion succeeds when they are equal. Otherwise, the test fails.
v1,v2-
Values to compare. Strings may contain
@{}expressions, which are evaluated when the event executes. errorMessage-
Optional string. The failure message; it may contain
@{}expressions. If omitted, a message showing the unequal values is used.
rtv.assertLtEvent(v1, v2, errorMessage)
Creates and returns an event that converts v1 and v2 to numbers and asserts that v1 < v2.
v1,v2-
Values to compare numerically. Strings may contain
@{}expressions, which are evaluated when the event executes. errorMessage-
Optional string. The failure message; it may contain
@{}expressions. If omitted, a message showing the failed comparison is used.
rtv.assertLeEvent(v1, v2, errorMessage)
Creates and returns an event that converts v1 and v2 to numbers and asserts that v1 ⇐ v2.
v1,v2-
Values to compare numerically. Strings may contain
@{}expressions, which are evaluated when the event executes. errorMessage-
Optional string. The failure message; it may contain
@{}expressions. If omitted, a message showing the failed comparison is used.
rtv.assertEvent(v1, v2, testFunction, alias, errorMessage)
Creates and returns an event that evaluates v1 and v2, passes them to testFunction, and succeeds or fails according to the returned boolean. Returning a non-boolean value produces an execution error.
v1,v2-
Values passed to
testFunction. Strings may contain@{}expressions, which are evaluated first. testFunction-
Function accepting the evaluated values and returning
trueorfalse. alias-
Optional string used to identify the assertion in analysis graphs.
errorMessage-
Optional string. The failure message; it may contain
@{}expressions. Defaults toAssertion failed.
rtv.log.infoEvent(text)
Creates and returns an event that logs text at the INFO level when requested and executed.
rtv.log.fineEvent(text)
Creates and returns an event that logs text at the FINE level when requested and executed. FINE messages are printed only in --verbose mode.
rtv.log.warnEvent(text)
Creates and returns an event that logs text at the WARN level when requested and executed.
rtv.log.errorEvent(text)
Creates and returns an event that logs text at the ERROR level when requested and executed.
For all four logging event methods, text must be a string or number. Strings may contain @{} expressions, which are evaluated when the event executes. As with the other event factory methods, call request(rtv.log.infoEvent(text)) (or fineEvent/warnEvent/errorEvent) to add the returned event to a scenario. An ERROR log entry does not by itself fail or error the test.
Methods
rtv.doStore(varName, value)
Stores value into runtime variable varName. In the typical use-case, runtime variables are updated by an automation layer, e.g. when a REST API requests a customer id from the system under test, and then uses that id during the rest of the test scenario. This command, which updates a runtime variable with a variable value from scenario generation time, serves a different use-case. It is typically used to reduce the model state space (see example) or to simulate sampled values from a currently unavailable source.
varName-
String. The name of the runtime variable. The name is evaluated when the event executes; the resulting name must be a valid JavaScript identifier and must not be a reserved JavaScript word.
value-
Any JavaScript value. Strings containing
@{}expressions, and fields of JavaScript objects, are evaluated when the storage event executes.
Example
The bthread below uses the runtime variable system to reduce states.
bthread("writer", function(){
let food = select("food").from( (1)
"Glazed Donuts",
"Jelly Donuts",
"Bavarian Cream-Filled Donuts",
"Bear Claws"
);
rtv.doStore("runtimeFood", food); (2)
food = null; (3)
request(Event("No, we're out of @{runtimeFood}")); (4)
});
-
A food type is selected using
select().from()an is place intofood, a regular variable. -
The value from
foodis stored into theruntimeFoodruntime variable. This is requests an event that will later appear in the test scenario, like any other event. -
The local variable is set to
null, reducing the state space -
The runtime variable is used via the
@{}syntax.

rtv.assertEq(expr1, expr2, errorMessage)
Requests the event returned by rtv.assertEqEvent. The assertion compares the string representations of expr1 and expr2 for equality.
expr1-
Number or String. May contain
@{}expressions. expr2-
Number or String. May contain
@{}expressions. errorMessage-
Optional string. The message to display if the assertion fails.
Example
Checking that both the API and the Web UI agree on an account balance. Assume browser is a Selenium session, and api is a REST API session.
bthread("account-balance-correct", function(){
browser.store("uiBalance", XPATH_TO_BALANCE);
api.get("/balance", {
callback: function(response){
pvg.rtv.set("apiBalance", response.body);
}
});
rtv.assertEq("@{uiBalance}", "@{apiBalance}", "Balances on API and UI do not match");
});
rtv.assertLt(expr1, expr2, errorMessage)
Requests the event returned by rtv.assertLtEvent. Both expressions are converted to numbers before checking that expr1 < expr2.
expr1-
Number or String. May contain
@{}expressions. expr2-
Number or String. May contain
@{}expressions. errorMessage-
Optional string. The message to display if the assertion fails.
Example
Asserting that the user account balance is greater than 10.
bthread("account-balance-correct", function(){
browser.store("balance", XPATH_TO_BALANCE);
rtv.assertLt("10", "@{balance}", "User balance should be greater than 10.");
});
rtv.assertLe(expr1, expr2, errorMessage)
Requests the event returned by rtv.assertLeEvent. Both expressions are converted to numbers before checking that expr1 ⇐ expr2.
expr1-
String that may contain
@{}expressions. expr2-
String that may contain
@{}expressions. errorMessage-
Optional string. The message to display if the assertion fails.
Example
Asserting that the user account balance is greater or equals to 10.
bthread("account-balance-correct", function(){
browser.store("balance", XPATH_TO_BALANCE);
rtv.assertLe("10", "@{balance}", "User balance should be greater than or equal to 10.");
});
rtv.assert(expr1, expr2, testFunction, alias, errorMessage)
A generalized way of asserting over runtime values. Requests the event returned by rtv.assertEvent. The assertion succeeds only when testFunction returns the boolean value true; it fails on false and produces an execution error for any other return value.
expr1-
Value passed to
testFunction. Strings may contain@{}expressions. expr2-
Value passed to
testFunction. Strings may contain@{}expressions. testFunction-
Function. Takes two parameters (
expr1andexpr2), returns a boolean (true/false). If it returnsfalse, the assertion fails. alias-
Optional string used to identify this assertion in analysis graphs.
errorMessage-
Optional string. The message to display if the assertion fails.
Example
Asserting that the sum of two runtime variables is even. Values were populated on different bthreads.
bthread("must be even", function(){
rtv.assert("@{varA}", "@{varB}", function(a, b){ return (a+b)%2==0; } )
});
rtv.log.LEVEL(text)
Logs runtime values during test execution. Logging is available at four levels: fine, info, warn, and error. Messages logged to fine are only printed when running in --verbose mode. All messages are stored in the logs.
Useful for inspecting calculations performed and values sampled from tested system during test execution.
The available requesting methods are rtv.log.fine(text), rtv.log.info(text), rtv.log.warn(text), and rtv.log.error(text). Each requests the event returned by its corresponding rtv.log.LEVELEvent(text) method.
text-
String or number to log. Strings may contain
@{}expressions, which are evaluated when the event executes.
Example
Logging at the various levels during execution:
// Performs an API call and stores the data
bthread("performApiCall", function(){
session.post("/get-quote", {
callback: function(r){
let body = JSON.parse(r.body);
pvg.rtv.set("quote", body.quote); (1)
}
});
rtv.log.info("Suggested quote is @{quote}"); (2)
rtv.log.warn("Tax on quote is @{quote*0.17}"); (3)
});
| 1 | Querying the system-under-test for a quote. |
| 2 | Logging the quote at the INFO level. |
| 3 | Logging an expression based on the quote at the WARN level. |
rtv.run(processFunction)
Requests an event that performs general processing of runtime values. For example, it can extract numbers hidden in text paragraphs so they can be asserted later.
processFunction-
Function. Receives an object that can read and write to the runtime values table, using methods
get(varName)andset(varName, value).
Example
browser.store("userStats", XPATH_TO_USER_STATS) (1)
rtv.run(function(values) {
let userCount = extractUserCount(values.get("userStats")); (2)
values.set("userCount", userCount); (3)
});
request(Event("User count is @{userCount}")); (4)
| 1 | Sample user statistics text from a web ui |
| 2 | Read that value, and call a function to extract user count |
| 3 | Store the clean number in a new runtime variable |
| 4 | Using the new variable value as usual. |