Logging Incoming Short Messages Steps¶
The SMS logging capability logs all incoming short messages within a scenario. This is used in test cases where unexpected short messages (such as "welcome" messages) arrive during the test run. It saves all incoming short messages received by a phone as a Context Variable, where they can be retrieved. The short message log list's data structure allows for filtering for messages that satisfy given conditions.
Logging Incoming SMS Step ¶
Syntax
1 | make short message log of <phone Identifier> available as <smsLogVariable String> |
Parameters
-
phone - The name assigned to the phone receiving the short messages
-
smsLogVariable - The name assigned to the Context Variable where all incoming short messages are logged
Example
1 | And make short message log of A available as MSG_A |
When this step is present anywhere in a scenario, all arriving short messages in the scenario are logged. This list generally grows during the test run, so the Context Variable's content changes with time.
Available Filtering Methods ¶
These methods are called on a short message log list (as with MSG_A
in the previous example). The methods filter a given short message log list so that the filtered result only contains messages that satisfy one or more given conditions. These methods may be chained.
Additional methods can be performed on filter methods. These are described below in Available Methods for Filtered Short Messages.
Text, Text Contains and Regex Text Matches ¶
These methods match short messages whose content equals defined criteria.
Syntax
1 2 3 4 5 6 | // Matches short messages whose content is exactly the same as the specified text <smsLogVariable Identifier>.text(<content String>) // Matches short messages whose content contains the specified snippet/excerpt <smsLogVariable Identifier>.textContains(<content String>) // Matches short messages whose content matches the regular expressions <smsLogVariable Identifier>.textMatches(<regex String>) |
Parameters
-
smsLogVariable - The name assigned to the Context Variable where all incoming short messages are logged
-
content - The SMS' content to be fully or partially matched against the short messages
-
regex - The regular expression to match against the ticket
- The regex must match the entire content to yield a positive result
Example
1 2 3 | And verify MSG_A.text("Your invoice is available.") And verify MSG_B.textContains("Hello").afterEpochSeconds(1000) And await MSG_C.textMatches("\\d+").size()==2 or timeout after 60 seconds |
From, From Contains and Regex From Matches ¶
These methods match short messages whose originating phone numbers equals defined criteria.
Syntax
1 2 3 4 5 6 7 | // Matches messages whose originating number is exactly the same as the // specified string <smsLogVariable Identifier>.from(<phoneNumber String>) // Matches short messages whose originating number matches the specified snippet/excerpt <smsLogVariable Identifier>.fromContains(<phoneNumber String>) // Matches short messages whose originating number matches the regular expressions <smsLogVariable Identifier>.fromMatches(<regex String>) |
Parameters
-
smsLogVariable - The name assigned to the Context Variable where all incoming short messages are logged
-
phoneNumber - The originating phone number to be fully or partially matched
-
regex - The regex to match against the originating number
Example
1 2 3 | And verify messages.from("orig1").size And verify messages.fromContains("+4312345") And verify messages.fromMatches("^..ig1$") |
After, After Seconds and After Milliseconds ¶
These methods match messages whose arrival timestamp is after a specified date.
Syntax
1 2 3 4 5 6 7 8 | // Matches short messages whose timestamp (of arrival) is after the given date <smsLogVariable Identifier>.after(<date ZonedDateTime>) // Matches short messages whose arrival time stamp is after the instant given // by the Unix epoch time in seconds <smsLogVariable Identifier>.afterEpochSeconds(<seconds Number>) // Matches short messages whose arrival time stamp is after the instant given // by the Unix epoch time in milliseconds <smsLogVariable Identifier>.afterEpochMillis(<milliseconds Number>) |
Parameters
-
smsLogVariable - The name assigned to the Context Variable where all incoming short messages are logged
-
date - The date in ZoneDateTime
-
seconds - The number of seconds
-
milliseconds - The number of milliseconds
Example
1 2 3 | And verify messages.after(now) And verify messages.afterEpochSeconds(1000) And verify messages.afterEpochMillis(100000) |
Before, Before Seconds and Before Milliseconds ¶
These methods match messages whose arrival timestamp is before a specified date.
Syntax
1 2 3 4 5 6 7 8 | // Matches short messages whose timestamp (of arrival) is before the given date <smsLogVariable Identifier>.before(<date ZonedDateTime>) // Matches short messages whose arrival time stamp is before the instant given // by the Unix epoch time in seconds <smsLogVariable Identifier>.beforeEpochSeconds(<seconds Number>) // Matches short messages whose arrival time stamp is before the instant given // by the Unix epoch time in milliseconds <smsLogVariable Identifier>.beforeEpochMillis(<milliseconds Number>) |
Parameters
-
smsLogVariable - The name assigned to the Context Variable where all incoming short messages are logged
-
date - The date in ZoneDateTime
-
seconds - The number of seconds
-
milliseconds - The number of milliseconds
Example
1 2 3 | And verify messages.before(now) And verify messages.beforeEpochSeconds(1000) And verify messages.beforeEpochMillis(100000) |
Available Methods for Filtered Short Messages ¶
The following methods can be chained to the list of filtered short messages. These can also be applied to the entire list of unfiltered messages. However, in most use cases, the method receiver will be a filtered list.
Size ¶
This method returns the number of elements in the filtered short message log list.
Syntax
1 | <filteredMessage Function>.size() |
Parameter
- filteredMessage - The function comprised of the message variable and any additional methods chained to the
size()
method
Example
1 | MYMESSAGES.textMatches("\\d+").size() |
The example above only looks for messages only consisting of numbers.
Get ¶
Returns the element of the list at the specified position. The index starts at 0
.
Parameters
-
filtered - The function comprised of the message variable and any additional methods chained to the
get()
method -
getIndex - The index position from where the element should be returned
Example
1 | MYMESSAGES.text("Hello").get(1) |
In the example above, the expression returns the second element of the filtered list. For example, the second message whose content equals "Hello"
. If the get()
method is ommitted, then all short messages whose content equals "Hello"
will be returned.
First and Last Element ¶
These methods return the first or last element of the list.
Syntax
1 2 3 4 | // Returns the list's first element <filteredMessage Function>.first() // Returns the list's last element <filteredMessage Function>.last() |
Parameter
- filteredMessage - The function comprised of the message variable and any additional methods chained to the
first()
orlist()
method
Example
1 2 | And verify MSG_B.textContains("result:").first() And verify messages.textContains("text").afterEpochMillis(101000).last() |
Additional Get Methods ¶
Any given single element of a short message log list is a ShortMessage data structure that can be accessed with the following methods.
Syntax
1 2 3 4 5 6 7 8 9 10 | // Returns the SMS' text/content <filteredMessage Function>.getText() // Returns the destination device's phone number, for example, the phone that // received the short message as a string <filteredMessage Function>.getNumber() // Returns the sending phone's number as a string. <filteredMessage Function>.getOriginatingNumber() // Returns the Unix Epoch Time in milliseconds as a long value // (the number of milliseconds since `1.1.1970 00h00m00s`). <filteredMessage Function>.getTimestamp() |
Example
1 2 3 4 | And verify MSG_A.textContains("result:").first().getText() == "result:579" And verify MSG_B.text("Hello").getNumber() And verify MYMESSAGES.textMatches("\\d+").getOriginatingNumber() And verify MYMESSAGES.before(now).getTimestamp() |
Await Conditions ¶
This expression waits until a certain number of messages with given conditions have arrived. More information is available in the Await Condition Steps section.
Syntax
1 | await <condition Expression> or timeout after <timeout Number> <units TimeUnit> |
Parameters
-
condition - The expression representing the expected state
-
timeout - The maximum amount of time units to wait for the condition
-
units - May be one of
milliseconds
,second
,seconds
,minute
,minutes
,hour
orhours
Example
1 | await MYMESSAGES.textContains("bonjour").size()==2 or timeout after 60 seconds |
The example above waits until two messages arrive whose text contains the word "bonjour"
. If this condition is not met after 60
seconds, the step fails.
Text Case Example ¶
The example below contains a Feature File and a Stepdef. The Scenario uses two phones, A
and B
. B
sends two short messages to A
, each of which only contain integer numbers (123
and 456
). A
waits for these messages, extracts the numbers and adds the two numbers to yield a result. This result is sent back to B
(with the text "result:579"
).
Feature File Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | Feature: SMSLogger Scenario: B asks A to add two numbers using SMS # Allocate 2 phones, A and B Given an Android phone as A And an Android phone as B # save the incoming short messages to MSG_A and MSG_B And make short message log of A available as MSG_A And make short message log of B available as MSG_B And B sends a short message to A as FIRSTOP: * with text "123" And within 10 seconds, expect the short message FIRSTOP to be sent And B sends a short message to A as SECONDOP: * with text "456" # A waits for these messages ... And await MSG_A.textMatches("\\d+").size()==2 or timeout after 60 seconds And define OPERANDS as MSG_A.textMatches("\\d+") # ... extracts the numbers and performs the addition And compute result of OPERANDS as RESULT And A sends a short message to B as RES: * with text "result:" + RESULT # B waits for the result and checks it it is correct And await MSG_B.textContains("result:").size() == 1 or timeout after 60 seconds And verify MSG_B.textContains("result:").first().getText() == "result:579" |
In the example above:
-
The steps
And make short message log of A available as MSG_A/MSG_B
make the incoming logged messages go to the short message log lists calledMSG_A
andMSG_B
. -
A
waits for the messages comprised only of numbers by using the stepAnd await MSG_A.textMatches("\\d+").size()==2 or timeout after 60 seconds
. -
As soon as the messages have arrived, the filtered list is copied to a Context Variable called
OPERANDS
by usingAnd define OPERANDS as MSG_A.textMatches("\\d+")
.
Stepdef Example
A custom Steps Language Stepdef for the Feature File above performs the actual calculation.
1 2 3 4 5 6 7 | stepdef "compute result of {} as {ident}" / operands, res / op1 := toNumber(operands.get(0).getText()) op2 := toNumber(operands.get(1).getText()) println("op1= " + op1 + "; op2= " + op2) setContextObject(res, toString(op1 + op2)) println("RESULT is " + getContextObject(res)) end |
In the example above, the operands
are extracted and converted to numbers. The addition result is converted back to a string and stored in the Context Object under parameter res
.