Test-Driven Development: Salesforce Flow Design Patterns
25 Oct 2022
Record-Triggered Flow on Account
Save
Give it a name
Close the alert titled “Review these issues” warning that “These issues prevent activation.” with a bullet point of “The flow can’t run because nothing is connected to the Start element.”
Click the “View Tests” button, upper right.
Click the “Create” button in the “Tests” popup.
In the “Set Test Details, Trigger, and Path” tab:
- Label: “Japan first-time”
- API Name: Let it compute “Test_Driven_Development_Japan_first_time”
- Description: “First-timers in Japan”
- Run the Test When a Record Is: “Updated” (other choice: “Created”)
- Path for Test: “Run Immediately” (it’s the only choice)
In the “Set Initial Triggering Record” tab:
- Account Name: “Happy Blocks”
In the “Set Updated Triggering Record” tab (only exists w/ “Updated” choice above):
- Account Name: Leave it filled in as “Happy Blocks”
- Billing Country: “Japan”
In the “Set Assertions” tab:
Under “Assertion: 1”:
- Resources (note you won’t be able to get at child records yet because you haven’t fleshed anything out – this is a little different than in Apex TDD): “$Record (Account)” > “ShippingCountry (Shipping Country)”, a.k.a. “
{!$Record.ShippingCountry}
” - Operator: “Equals” (other options: “Does Not Equal”, “Starts With”, “Ends With”, “Contains”, “Is Null”, “Is Changed”, “Was Set”)
- Value: “Japan” (Interesting – there’s no
{!$Record.BillingCountry}
option – that gets me “Enter a valid value.” – just typing a string,{!$GlobalConstant.EmptyString}
,{!$GlobalConstant.False}
, and{!$GlobalConstant.True}
.)
We’ll skip over Assertion #1’s “Add Custom Failure Message” button & the “Add Assertion” button.
Click the popup box’s “Save” button.
Dangit, it errors out with a big red do-not-enter toast at the top center saying, “The Test-Driven Development Flow is missing a path that runs immediately.”
I’ll close it out and rebuild it again later.
I add 1 “Assignment” element:
- Label: “Just playing”
- Variable: “
{!$Record.BillingCity}
” - Operator: “Equals” (other choice: “Add”)
- Value: “Just playing around”
The “View Tests” button is grayed out, but clicking “Save” on the flow brings it back.
OK, now I was able to create a test – the window closes out so I have to click the “View Tests” button again.
I check the checkbox to the left of the “Japan first-time” entry in the list and click the “Run” button in the bottom-right of the popup.
There’s a green checkbox toast that auto-disappears at the top center of the screen saying “The selected tests were run.”
In the test list, I can see that the “Result” of the “Japan first-time” entry in the list is “Fail.”
I click the dropdown arrow at the far right of the “Japan first-time” entry in the list and click “Run Test and View Details”.
In the center of my screen, I can see an orange thick line running from “Start” to “End” through “Just Playing.”
In the right-hand panel of my screen, which is labeled “Test Run Details,” in its “Assertions” tab, I see a section titled “Assertion 1” with a red oval labeled “Failed”. It says:
The assertion failed because a condition was not met.
Condition
{!$Record.ShippingCountry} Equals Japan
Condition evaluation
{!$Record.ShippingCountry} (null) Equals Japan
In the “Test Run Details” tray’s “All Details” tab, there are a lot of sections to explore:
- How the Interview Started
User User (0055500000BVUTu) started the flow interview.
API Version for Running the Flow: 56
Some of this flow’s variables were set when the interview started.
$Record = Account (0015500001WOw0eAAD)
versionId:30155000000VsiR
Flow start time: October 25, 2022 at 9:02 AM. - Start Condition Requirements
The triggering record met the condition requirements.
Entry Conditions
null - Assignment: Just playing
{!$Record.BillingCity} Equals Just playing around
Result
{!$Record.BillingCity} = “Just playing around” - Assertion 1 (“failed” red oval)
The assertion failed because a condition was not met.
Condition
{!$Record.ShippingCountry} Equals Japan
Condition evaluation
{!$Record.ShippingCountry} (null) Equals Japan
This is a good thing!
Let’s click the “View Tests” button in the upper-right corner of the screen.
We’ll click “Create” and create a whole bunch more tests:
TODO:
Now we’ve got a whole suite of tests we can run at the same time – click the checkbox at the far upper-left corner of the test list, to the left of the “Test Label” column heading, then click the “Run” button in the bottom-right of the “Tests” popup.
All of them should flip to having a Result of “Fail.”
This is also a good thing!
Congratulations – have now done the “test-driven” part of “test-driven development.”
You just have to do the “development” part.
Close out of the “Tests” popup with the “Cancel” button or its “X” in the upper-right corner, if you’ve still got a “Test Run Details” panel, click the “Edit Flow” button in the top right of the Flow.
I also recommend downloading a copy of your flows & flowtests at this point.
.../flows/Test_Driven_Development.flow-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<Flow xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>56.0</apiVersion>
<assignments>
<name>Just_playing</name>
<label>Just playing</label>
<locationX>176</locationX>
<locationY>335</locationY>
<assignmentItems>
<assignToReference>$Record.BillingCity</assignToReference>
<operator>Assign</operator>
<value>
<stringValue>Just playing around</stringValue>
</value>
</assignmentItems>
</assignments>
<environments>Default</environments>
<interviewLabel>Test-Driven Development {!$Flow.CurrentDateTime}</interviewLabel>
<label>Test-Driven Development</label>
<processMetadataValues>
<name>BuilderType</name>
<value>
<stringValue>LightningFlowBuilder</stringValue>
</value>
</processMetadataValues>
<processMetadataValues>
<name>CanvasMode</name>
<value>
<stringValue>AUTO_LAYOUT_CANVAS</stringValue>
</value>
</processMetadataValues>
<processMetadataValues>
<name>OriginBuilderType</name>
<value>
<stringValue>LightningFlowBuilder</stringValue>
</value>
</processMetadataValues>
<processType>AutoLaunchedFlow</processType>
<start>
<locationX>50</locationX>
<locationY>0</locationY>
<connector>
<targetReference>Just_playing</targetReference>
</connector>
<object>Account</object>
<recordTriggerType>CreateAndUpdate</recordTriggerType>
<triggerType>RecordAfterSave</triggerType>
</start>
<status>Draft</status>
</Flow>
.../flowtests/Test_Driven_Development_Another_test.flowtest-meta.xml
- Description
- Label
- FlowAPIName
- TestPoints (x2)
- ElementApiName of “Start” w/ 1 Parameters node in it (one of type “InputTirggeringRecordInitial”)
- ElementApiName of “Finish” w/ 1 Assertions node in it
<?xml version="1.0" encoding="UTF-8"?>
<FlowTest xmlns="http://soap.sforce.com/2006/04/metadata">
<flowApiName>Test_Driven_Development</flowApiName>
<label>Another test</label>
<testPoints>
<elementApiName>Start</elementApiName>
<parameters>
<leftValueReference>$Record</leftValueReference>
<type>InputTriggeringRecordInitial</type>
<value>
<sobjectValue>{"CleanStatus":"Pending","Name":"O hai","OwnerId":"0055500000BVUTuAAP"}</sobjectValue>
</value>
</parameters>
</testPoints>
<testPoints>
<assertions>
<conditions>
<leftValueReference>$Record.BillingLongitude</leftValueReference>
<operator>EqualTo</operator>
<rightValue>
<numberValue>1.0</numberValue>
</rightValue>
</conditions>
</assertions>
<elementApiName>Finish</elementApiName>
</testPoints>
</FlowTest>
.../flowtests/Test_Driven_Development_Here_we_go.flowtest-meta.xml
- Description
- Label
- FlowAPIName
- TestPoints (x2)
- ElementApiName of “Start” w/ 1 Parameters node in it (one of type “InputTirggeringRecordInitial”)
- ElementApiName of “Finish” w/ 1 Assertions node in it
<?xml version="1.0" encoding="UTF-8"?>
<FlowTest xmlns="http://soap.sforce.com/2006/04/metadata">
<flowApiName>Test_Driven_Development</flowApiName>
<label>Here we go</label>
<testPoints>
<elementApiName>Start</elementApiName>
<parameters>
<leftValueReference>$Record</leftValueReference>
<type>InputTriggeringRecordInitial</type>
<value>
<sobjectValue>{"CleanStatus":"Pending","Name":"wheeeee","OwnerId":"0055500000BVUTuAAP"}</sobjectValue>
</value>
</parameters>
</testPoints>
<testPoints>
<assertions>
<conditions>
<leftValueReference>$Record.BillingPostalCode</leftValueReference>
<operator>EqualTo</operator>
<rightValue>
<stringValue>12345</stringValue>
</rightValue>
</conditions>
</assertions>
<elementApiName>Finish</elementApiName>
</testPoints>
</FlowTest>
.../flowtests/Test_Driven_Development_Japan_first_time.flowtest-meta.xml
- Description
- Label
- FlowAPIName
- TestPoints (x2)
- ElementApiName of “Start” w/ 2 Parameters nodes in it (one of type “InputTirggeringRecordInitial,” another of type “InputTriggeringRecordUpdated”)
- ElementApiName of “Finish” w/ 1 Assertions node in it
<?xml version="1.0" encoding="UTF-8"?>
<FlowTest xmlns="http://soap.sforce.com/2006/04/metadata">
<description>First-timers in Japan</description>
<flowApiName>Test_Driven_Development</flowApiName>
<label>Japan first-time</label>
<testPoints>
<elementApiName>Start</elementApiName>
<parameters>
<leftValueReference>$Record</leftValueReference>
<type>InputTriggeringRecordInitial</type>
<value>
<sobjectValue>{"CleanStatus":"Pending","Name":"Happy Blocks","OwnerId":"0055500000BVUTuAAP"}</sobjectValue>
</value>
</parameters>
<parameters>
<leftValueReference>$Record</leftValueReference>
<type>InputTriggeringRecordUpdated</type>
<value>
<sobjectValue>{"BillingCountry":"Japan","CleanStatus":"Pending","Name":"Happy Blocks","OwnerId":"0055500000BVUTuAAP"}</sobjectValue>
</value>
</parameters>
</testPoints>
<testPoints>
<assertions>
<conditions>
<leftValueReference>$Record.ShippingCountry</leftValueReference>
<operator>EqualTo</operator>
<rightValue>
<stringValue>Japan</stringValue>
</rightValue>
</conditions>
</assertions>
<elementApiName>Finish</elementApiName>
</testPoints>
</FlowTest>
Tooling API (interestingly, flowApiName
does not have anything here):
SELECT Id
, FullName
, MasterLabel
, NamespacePrefix
, DeveloperName
, ManageableState
, Language
, Metadata
FROM FlowTest