5 BDD Functions

Learn about the different types of Behavior-Driven Development functions in Oracle Communications Solution Test Automation Platform (STAP).

Overview of BDD Functions

A BDD function is a pre-defined command set that performs an operation and returns a single value. These functions are useful while performing mathematical calculations, string Concatenations (Concat), sub-strings, JSON operations, and so on.

Allowing Commas in Function Data

Function arguments are separated by a comma (,). If the function arguments or variable values contain a comma, you can escape it using %{,}.

Escape only the values provided for function. If there is a comma in the context values or JSON property values escape is not required and done internally.

For example, if a comma is in the text for a variable value:

Save:
| subscriptions | Need to purchase 'premium%{,}active plan' from catalog on Tuesday and 'basic%{,}active plan on Wednesday' | 
 
  
Or if the argument to the pattern matching function contains a comma:
| secondPlan | %PATTERN_MATCHER(${subscriptions},'basic%{,}(.*?)',0) |

Using Response Properties and Variables in the Functions

Using Data from the Response

If you want to use a property from the response, you can access it by name if you are not using it inside a function. For example, you can assign the name property from the response to the firstName variable like this:

Save:
| firstName | name  |

However, when you are using that response property inside a function, you should use a dollar sign ($) before the name, like this:

Save:
| firstName | %LOWERCASE($name)|
Using Scenario Variables

To use saved scenario variables as function argument, use ${<variable>}. For example,

Save:
| firstName | %LOWERCASE($name)|
| updatedFirstName | %UPPERCASE(${firstName})|
Using functions in Validate property

You can use functions in validating both properties and values.

For example,

Validate:
|  %ARRAY_VALUE(subscriptions[?(@.status=='ACTIVE')].plan)  |  Premium   | 
|  %SUBSTRING(${notificationText},33) | test@example.com  |
Validate:
| plan | %SUBSTRING(${subscriptionPlan},0,7) |
| orderID | %PATTERN_MATCHER(${orderConfirmation},\d+,0) |

String Functions

String functions are used to manipulate and handle string data.

These functions take a string as an input argument and return a modified string:

SUBSTRING

The SUBSTRING function allows you to retrieve part of a string. You can either the part of a string that starts at a specified character number, or only a specified number of characters starting at a specified character. The format of the function is:

%SUBSTRING(string,beginIndex,noChars)

where:
  • string is either a text string or a variable
  • beginIndex is he number of the character from which to start reading the string. If noChars is not present, it will read to the end of the string. Set this to 0 to read from the beginning of the string.
  • noChars is optional and specifies the exact number of characters to read.
For example, after the commands below, the emailId variable contains the string test@example.com.
Save:
| notificationText | Notification sent at 10:30 AM to test@example.com |
 
Validate:
| emailID | %SUBSTRING(${notificationText},33) |

After the commands below, the plan variable contains Premium.

Save:
| subscriptionPlan | Premium Subscription Activated Successfully |
 
Validate:
| plan | %SUBSTRING(${subscriptionPlan},0,7) |

PATTERN_MATCHER

A pattern matcher retrieves a substring using a regular expression. In STAP, the regular expression used by the pattern matcher contains characters that need to be escaped. If these characters are not escaped, the publish scenario scripts might fail.

The following functions are used to extract specific substrings from a given string:

%PATTERN_MATCHER(<string>,<reg.exp>)

Retrieves a substring which matches the given regular expression pattern.

For example,

When set variable, get the Customer information
Save:
| userMessage | Important Notice : 'Your subscription is expiring soon' |
Validate:
| extractedNotice | %PATTERN_MATCHER(${userMessage},'(.*?)',0) |
 
extractedNotice returns 'Your subscription is expiring soon'
%PATTERN_MATCHER(<string>,<reg.exp>,index)

Retrieves a sub string at the index from the set of matches for a regular expression pattern.

For example,

When set variable, get the Customer information
Save:
| orderConfirmation | Order #INV-12345 confirmed for your subscriptionPlan |
Validate:
| orderID | %PATTERN_MATCHER(${orderConfirmation},\d+,0) |
 
 orderID returns 12345 
%PATTERN_MATCHER(<string>,<reg.exp>,index,groupIndex)
  • index : index of the match
  • groupIndex : Group Index of the match

For example,

When set variable, get the Customer information
Save:
| notificationText | Notification sent at 10:30 AM to test@example.com |
Validate:
| emailDomain | %PATTERN_MATCHER(${notificationText},@([\w-]+)\.com,0,1) |
 
emailDomain returns example

Replace

The following string manipulation function is used to replace text dynamically:

%REPLACE(<search string>,<replace string>)

Replaces all occurrences of the given search string with replace string.

For example,

When set variable, get the Customer information
Save:
| notificationText | Notification sent at 10:30 AM to test@example.com |
 
Validate:
| modifiedNotification | %REPLACE(${notificationText},test@example.com,anonymous@example.com) |
 
modifiedNotification returns Notification sent at 10:30 AM to anonymous@example.com

Replace First

The following string manipulation function is used to replace text dynamically:

%REPLACE_FIRST(<search string>,<replace string>)

Replaces the first occurrence of the given search string with replace string.

For example,

When set variable, get the Customer information
Save:
| orderConfirmation | Order #INV-12345 confirmed for your subscriptionPlan |
 
Validate:
| modifiedOrder | %REPLACE_FIRST(${orderConfirmation},O,BO) |
 
modifiedOrder returns Border #INV-12345 confirmed for your subscriptionPlan

Concat

The following string concatenation function is used to join multiple string arguments into a single string. It helps merge different pieces of text dynamically.

%CONCAT(<arg1>,<arg2>[,<arg3>...]) : Concatenate the given string arguments.

For example,

When set variable, getting Customer information
Save:
| subscriptionPlan | Premium Subscription Activated Successfully |
| billingDetails | Your next billing date is 15-03-2025 |
 
Validate:
| finalMessage | %CONCAT(${subscriptionPlan} ,${billingDetails}) |
 
finalMessage returns Premium Subscription Activated Successfully Your next billing date is 15-03-2025

Uppercase and Lowercase

These functions are used to convert the string into Lowercase or Uppercase.

%LOWERCASE(<string>) :

Converts the given string into lowercase

%UPPERCASE(<string>) :

Converts the given string into uppercase

For example,

When set variable, getting Customer information
Save:
| subscriptionPlan | Premium Subscription Activated Successfully |
| billingDetails | Your next billing date is 15-03-2025 |
 
Validate:
| planName | %LOWERCASE(${subscriptionPlan}) |
| nextBilling | %UPPERCASE(${billingDetails}) |
 
planName returns premium subscription activated successfully
nextBilling returns YOUR NEXT BILLING DATE IS 15-03-2025

Numeric Functions

Numeric functions help perform operations on numbers in various sections, including Data, Save, and Validate. They assist in rounding numbers and generating random values dynamically. For supported arithmetic expression, see Numeric Function: Evaluate to Process Arithmetic Expressions.

Rounding Numbers (%ROUND(<arg1>))

This function rounds the given numeric input to the nearest whole number (long numeric value).

For example, %ROUND(3.6) - Returns 4.

Refer to the following BDD Example:

When set variables,
Save:
| chocolates | 3.6 |
 
When buy chocolates,
Data:
| number | %ROUND(${chocolates}) |

Generating Random Numbers (%RANDOM())

This function returns a pseudorandom double greater than or equal to 0.0 and less than 1.0

For example, %RANDOM() - Returns 0.753524282283047

Refer to the following BDD Example:

When buy chocolates,
Data:
| number | %RANDOM() |

Numeric Function: Evaluate to Process Arithmetic Expressions

STAP supports all standard arithmetic operations, such as +,-,*,/. Specify the expression in reverse polish notation or postfix notation.

STAP requires the postfix operation for its arithmetic operations for the following reasons:

  • Postfix notations are easier to parse for compiler
  • Rules out the need for left - right association and precedence
  • Faster to evaluate (less time for parsing)
  • Can be expressed without parenthesis
  • No 3rd party library dependency required

Using Arithmetic Operations

You must use the following format to perform arithmetic operations:

%EVAL(<arithmetic_operations_written_in_reverse_polish_notation>)
 
# each operand and operator should be comma separated
# to pass in STAP variables use: ${<variable>}
 
Example:
# (2+1)*3
| name | %EVAL(2,1,+,3,*) |
# (arg3+arg5)
| name | %EVAL(${arg3},${arg5},+) |
The following example shows how to evaluate expressions using arithmetic operations:
Case: Evaluate Expressions

When set variable, saving various signal datas into variables
Save:
| arg1 | 10 | 
| arg2 | 9  |
| arg3 | 4  | 
| arg4 | 2  | 
| arg5 | 14 | 
| arg6 | 20 |  
 
When set variable, evaluating various communication fields 
Save: 
#|  Property         |  Value                                                             |  Runtime Value  | 
 |  signalQuality    |  %EVAL(2,1,+,3,*)                                                  |  9              | 
 |  transmissionRate |  %EVAL(${arg3},${arg5},+)                                          |  18             | 
 |  networkLatency   |  %EVAL(${arg1},${arg2},+,${arg3},*)                                |  76             | 
 |  packetDropRate   |  %EVAL(${arg1},${arg2},${arg3},*,${arg4},${arg5},-,/,${arg6},*,+)  |  -50            | 

JSON and Response Functions

JSON functions perform operations on response JSON. These can be used in Validate or Save blocks. JSON functions include the following:
  • Array value
  • Array size
  • Response header

Array Value:

This function retrieves elements from an array using JSON Path:

  • %ARRAY_VALUE(<JSON Path>): Returns the first element in the array resolved by the JSON Path.
  • %ARRAY_VALUE(<JSON Path>, <index>): Returns the index element in the array resolved by the JSON Path. Index starts from 0.

The following is the response body in JSON format:

{
   "user": "John Doe",
   "email": "john@billing.com",
   "subscriptions": [
   {"plan": "Premium", "status": "ACTIVE", "expiry": "2025-03-15"},
   {"plan": "Basic", "status": "EXPIRED", "expiry": "2024-01-01"}
   ]
}

The following are some examples of Array Value:

  • Get the first email for the matched JSON Path

    %ARRAY_VALUE(emails[?(@.status == 'VERIFIED')].email) returns first@email

  • Get the email at index 1 for the matched JSON Path

    %ARRAY_VALUE(emails[?(@.status == 'VERIFIED')].email,1) returns third@email

  • Get the value at index 1 for the matched JSON Path

    %ARRAY_VALUE(emails[?(@.status == 'VERIFIED')].value,1) returns 30

The following is a BDD example for an Array Value:

Then get mock response, processing Customer subscribed date and subscription details
Validate:
| firstPlan | %ARRAY_VALUE(subscriptions[?(@.status=='ACTIVE')].plan) |
| activePlanExpiry | %ARRAY_VALUE(subscriptions[?(@.status=='ACTIVE')].expiry,0) |

The following is the runtime BDD response:

Validate:
#| Property | Value  | Property Value | Runtime Value | Result | 
 | firstPlan | %ARRAY_VALUE(subscriptions[?(@.status=='ACTIVE')].plan) Premium | Premium | PASSED | 
 | activePlanExpiry | %ARRAY_VALUE(subscriptions[?(@.status=='ACTIVE')].expiry,0)| 2025-03-15 | 2025-03-15 | PASSED | 
 | subscriptionCount | %ARRAY_SIZE(subscriptions) | 2 | 2 | PASSED |

Array Size

This function returns the number of elements in an array.

%ARRAY_SIZE(<JSON Path>) : Returns the size of the array returned by the JSON path

For example,

Validate:
#|  Property          |  Value                      |  Property Value  |  Runtime Value | Result |
 |  subscriptionCount |  %ARRAY_SIZE(subscriptions) |  2               |  2              |  PASSED  |

Returns: 2 (since there are two subscription entries)

Response Header

This function returns the value for the given header key, if it is present in response headers.

For example,

{
...
"headers" : {
              "transfer-encoding" : "chunked",
              "connection" : "keep-alive",
              "Date" : "Wed, 25 Aug 2021 04:51:40 -0700",
              "Content-Type" : "application/json"
            }
...
}

Get the Date header from response.

%RESPONSE_HEADER(Date) returns "Wed, 25 Aug 2021 04:51:40 -0700"

The following is a BDD example for using %RESPONSE_HEADER() in Save block:

Then get mock response, processing Customer subscription details
Save:
|  Date | %RESPONSE_HEADER(Date)    |
| Connection | %RESPONSE_HEADER(connection) |  

The following is the runtime BDD response for using %RESPONSE_HEADER() in Save block:

Then get mock response, processing Customer subscription details
 Save:
#|  Property   |  Value                       |  Runtime Value                               |  
|  Date        | %RESPONSE_HEADER(Date)       |  Wed, 25 Aug 2021 04:51:36 -0700             |
| Connection   | %RESPONSE_HEADER(connection) |  keep-alive                                  |

The following is a BDD example for using %RESPONSE_HEADER() in Validate block:

Then get mock response, processing Customer subscription details
Validate:
| %RESPONSE_HEADER(connection)  | ${connection} |

The following is the runtime BDD response for using %RESPONSE_HEADER() in Validate block:

Then get mock response, processing Customer subscription details
Validate:
#|  Property                   |  Value            |  Property Value     |  Runtime Value          |  Result           | 
| %RESPONSE_HEADER(connection) | ${connection}     |  keep-alive         |      keep-alive         |      PASSED       |

Data Type Functions

Data Type functions are used in Data block to represent the type of property value. By default, all data is treated as a string. To convert data to other types, use the appropriate data type functions.

Table 5-1 describes Data Type functions used in Data block to represent the type of property value.

Table 5-1 Data Type Functions

Function Description Example: Data
%INT(<int value>) To represent integer values %INT(200) -→ "value": 200
%DOUBLE(<double value>) To represent floating/double values %DOUBLE(35.75) -→ "billAmount": 35.75
%BOOLEAN(<boolean value>) To represent boolean values %BOOLEAN(true) →> "created" : true

Date Type Functions

These functions retrieve, modify, and transform dates in various formats and are useful for timestamping, scheduling, and handling date-based calculations.

Retrieve Current Date (%NOW())

Returns current date in YYYY-MM-ddTHH:mm:ss.SSSZ format. For example, %NOW() → "2021-08-25T14:16:28.312Z"

The following is a BDD example for retrieving current date:

When add todo task, for booking appointment

Data: Table 5-2 lists out the values in NOW format.

Table 5-2 NOW format

Property Value Runtime Value
description %NOW() 2021-08-25T14:16:28.312Z

Retrieve Current Date in a Custom Format (%NOW(<format>))

Returns current date in specified format. For example, %NOW(YYYY-MM-dd) → "2021-08-25"

The following is a BDD example for retrieving current date in a custom format:

When add todo task, for booking appointment

Data: Table 5-3 lists out the values in NOW format.

Table 5-3 NOW format

Property Value Runtime Value
description %NOW(YYYY-MM-dd) 2021-08-25

For more information on formatting the date, see Class SimpleDateFormat in Oracle Java documentation.

Add or Subtract Time (%NOWADD(<field>, <+/- value>))

Modifies the current date or time by adding or subtracting a specific amount from a time field.

Default format (YYYY-MM-dd'T'HH:mm:ss.SSS'Z')

For example, | dateTime | %NOWADD(5,10) | # Adds 10 units to field 5 | dateTime | %NOWADD(5,-10) | # Subtracts 10 units from field 5

Custom Format (%NOWADD(<field>, <+/- value>, <output format>))

For example, | dateTime | %NOWADD(5,10,yyyy-MM-dd HH:mm:ss) |

Output:
"2024-05-07 10:10:10"

Modify a Saved Date (%NOWADD(<field>, <+/- value><output format>))

Adds or Subtracts from a date field and returns date in specified format.

# Add or Subtract from current time using Custom Format

| dateTime | %NOWADD(5,10,yyyy-MM-dd HH:mm:ss) |

%DATEADD(<field>, <+/- value>)

Add/Subtract from a date field and returns date in default format YYYY-MM-dd'T'HH:mm:ss.SSS'Z'.

For example, | dateTime | %DATEADD(${datavar},5,5,yyyy-MM-dd HH:mm:ss) |

Advanced example, (%DATEADD(<field>, <+/- value>, <input format>, <output format>):

| dateTime | %DATEADD(2024-05-07 10:10:10,5,-5,yyyy-MM-dd HH:mm:ss,dd-MM-YYYY) |

Transforms: "2024-05-07 10:10:10" → "07-05-2024"

Transform Date Formats (%TRANSFORM(<date1><inputFormat><outputFormat>))

Transforms given date in the input format to specified output format.

The following BDD example uses Transform function to trasnform date in the Save section to specified format:

When execute mock action, reading the task
Data:
| $request | $arraydata2 |
Save:
| dateTime | %TRANSFORM(2024-05-07 10:10:10,YYYY-MM-dd HH:mm:ss,dd-MM-YYY) |

Format Number Functions

The Format Number function formats a numeric value according to a specified pattern, applying different rounding modes as needed such as FLOOR, CEILING, and ROUND. It supports various separators, custom decimal places, and string interpolation within the formatted output.

Table 5-4 describes variants of Format Number Functions.

Table 5-4 Variants and Descriptions

Variant Description
CEILING Rounding mode to round towards positive infinity.
DOWN Rounding mode to round towards zero.
FLOOR Rounding mode to round towards negative infinity.
HALF_DOWN Rounding mode to round towards nearest neighbor unless both neighbors are equidistant, in which case you round down instead.
HALF_EVEN Rounding mode to round towards the nearest neighbor unless both neighbors are equidistant, in which case, round towards the even neighbor.
HALF_UP Rounding mode to round towards nearest neighbor unless both neighbors are equidistant, in which case you round up instead.
UNNECESSARY Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary.
UP Rounding mode to round away from zero.

The following example shows the BDD code to format a number:

Case: Format Number
When set variable, customer bill value is taken as input
Save:
| price |  1234567.89 |
When set variable, to get formatted customer bill details
Save:
| formattedBill | %FORMAT_NUMBER(481.195) |
| decimalBill | %FORMAT_NUMBER(${price},0.0) |
| roundedBill | %FORMAT_NUMBER(${price},0) |
| roundedBill2 | %FORMAT_NUMBER(${price},#.##,CEILING) |
| roundedBill3 | %FORMAT_NUMBER(${price},#%{,}###.##,CEILING) |
| roundedBill4 | %FORMAT_NUMBER(${price},Amount to be payable is $#%{,}###.# for this month,CEILING) |
| discountedBill | %FORMAT_NUMBER(${price},#,FLOOR) |
| discountedBill1 | %FORMAT_NUMBER(${price},#,HALF_EVEN) |
| discountedBill2 | %FORMAT_NUMBER(${price},#,HALF_UP) |
| discountedBill3 | %FORMAT_NUMBER(${price},#,HALF_DOWN) |
Output (Runtime BDD):
When set variable, to get formatted customer bill details
Save:
#|  Property         |  Value                                                                                |  Runtime Value                                        |
 |  price            |  1234567.89                                                                           |  1234567.89                                           |
 |  test             |  12.053548387096775                                                                   |  12.053548387096775                                   |
 |  formattedBill    |  %FORMAT_NUMBER(481.195)                                                              |  481.20                                               |
 |  decimalBill      |  %FORMAT_NUMBER(${price},0.0)                                                         |  1234567.9                                            |
 |  roundedBill      |  %FORMAT_NUMBER(${price},0)                                                           |  1234568                                              |
 |  roundedBill2     |  %FORMAT_NUMBER(${price},#.##,CEILING)                                                |  1234567.89                                           |
 |  roundedBill3     |  %FORMAT_NUMBER(${price},#%{,}###.##,CEILING)                                         |  1,234,567.89                                         |
 |  roundedBill4     |  %FORMAT_NUMBER(${price},Amount to be payable is $#%{,}###.# for this month,CEILING)  |  Amount to be payable is $1,234,567.9 for this month  |
 |  discountedBill   |  %FORMAT_NUMBER(${price},#,FLOOR)                                                     |  1234567                                              |
 |  discountedBill1  |  %FORMAT_NUMBER(${price},#,HALF_EVEN)                                                 |  1234568                                              |
 |  discountedBill2  |  %FORMAT_NUMBER(${price},#,HALF_UP)                                                   |  1234568                                              |
 |  discountedBill3  |  %FORMAT_NUMBER(${price},#,HALF_DOWN)                                                 |  1234568                                              |

Format Patterns

DecimalFormat is a concrete subclass of NumberFormat that formats decimal numbers. It has a variety of features designed to parse and format numbers in any locale, including support for Western, Arabic, and Indic digits. It also supports different kinds of numbers, including integers (123), fixed-point numbers (123.4), scientific notation (1.23E4), percentages (12%), and currency amounts ($123). All of these can be localized.

To obtain a NumberFormat for a specific locale, including the default locale, use one of NumberFormat's factory methods, such as getInstance(). In general, avoid using the DecimalFormat constructors directly, since the NumberFormat factory methods may return subclasses other than DecimalFormat. A DecimalFormat comprises a pattern and a set of symbols. The pattern may be set directly using applyPattern(), or indirectly using the API methods. The symbols are stored in a DecimalFormatSymbols object. When using the NumberFormat factory methods, the pattern and symbols are read from localized ResourceBundles. To customize format object, perform the following action:

A DecimalFormat comprises a pattern and a set of symbols. The pattern may be set directly using applyPattern(), or indirectly using the API methods. The symbols are stored in a DecimalFormatSymbols object. When using the NumberFormat factory methods, the pattern and symbols are read from localized ResourceBundles.

Patterns
DecimalFormat patterns have the following syntax:
 Pattern:
         PositivePattern
         PositivePattern ; NegativePattern
 PositivePattern:
         Prefixopt Number Suffixopt
 NegativePattern:
         Prefixopt Number Suffixopt
 Prefix:
         any Unicode characters except \uFFFE, \uFFFF, and special characters
 Suffix:
         any Unicode characters except \uFFFE, \uFFFF, and special characters
 Number:
         Integer Exponentopt
         Integer . Fraction Exponentopt
 Integer:
         MinimumInteger
         #
         # Integer
         # , Integer
 MinimumInteger:
         0
         0 MinimumInteger
         0 , MinimumInteger
 Fraction:
         MinimumFractionopt OptionalFractionopt
 MinimumFraction:
         0 MinimumFractionopt
 OptionalFraction:
         # OptionalFractionopt
 Exponent:
         E MinimumExponent
 MinimumExponent:
         0 MinimumExponentopt

Understanding DecimalFormat Patterns

DecimalFormat patterns help format numerical values for proper display. They define prefixes, numeric values, and suffixes while handling positive and negative subpatterns, separators, and formatting symbols.

The following are the key features of DecimalFormat Patterns:

  • Contains positive and negative subpatterns (for example, `"#,##0.00;(#,##0.00)"`).
  • If no negative subpattern is provided, the positive pattern is prefixed with a localized minus sign (`'-'` in most locales).
  • Customizable prefixes and suffixes can be used for different formatting styles.
Here is the behavior of positive and negative subpatterns.
  • `"0.00"` is equivalent to `"0.00;-0.00"` since the minus sign is automatically applied.
  • If a negative subpattern is explicitly defined, only the prefix and suffix change while the numerical rules remain the same.

    For example, `"#,##0.0#;(#)"` behaves exactly the same as `"#,##0.0#;(#,##0.0#)"`.

Formatting Symbols and Separators

Symbols for infinity (`∞`), digits (`0-9`), thousand separators (`,`), and decimal points (`.`) are fully customizable. Care must be taken to avoid conflicts to ensure:

  • Positive and negative prefixes or suffixes are distinct for accurate parsing.
  • Decimal separator and thousand separator are unique to prevent errors.

Grouping Separators and their Behavior

Typically used for thousands, though some locales use them for ten-thousands. The grouping size determines the digit intervals.

For example, `3` for `"100,000,000"` or `4` for `"1,0000,0000"`.

If multiple grouping characters are provided, the last grouping separator before the integer end is used. For example, `"#,##,###,####"` == `"######,####"` == `"##,####,####"`.