Salesforce, Python, SQL, & other ways to put your data where you need it

Need event music? 🎸

Live and recorded jazz, pop, and meditative music for your virtual conference / Zoom wedding / yoga class / private party with quality sound and a smooth technical experience

Test-Driven Development: Salesforce Flow Design Patterns

25 Oct 2022 🔖 salesforce
💬 EN

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:

  1. 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.

  2. Start Condition Requirements

    The triggering record met the condition requirements.
    Entry Conditions
    null

  3. Assignment: Just playing

    {!$Record.BillingCity} Equals Just playing around
    Result
    {!$Record.BillingCity} = “Just playing around”

  4. 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)
    1. ElementApiName of “Start” w/ 1 Parameters node in it (one of type “InputTirggeringRecordInitial”)
    2. 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>{&quot;CleanStatus&quot;:&quot;Pending&quot;,&quot;Name&quot;:&quot;O hai&quot;,&quot;OwnerId&quot;:&quot;0055500000BVUTuAAP&quot;}</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)
    1. ElementApiName of “Start” w/ 1 Parameters node in it (one of type “InputTirggeringRecordInitial”)
    2. 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>{&quot;CleanStatus&quot;:&quot;Pending&quot;,&quot;Name&quot;:&quot;wheeeee&quot;,&quot;OwnerId&quot;:&quot;0055500000BVUTuAAP&quot;}</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)
    1. ElementApiName of “Start” w/ 2 Parameters nodes in it (one of type “InputTirggeringRecordInitial,” another of type “InputTriggeringRecordUpdated”)
    2. 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>{&quot;CleanStatus&quot;:&quot;Pending&quot;,&quot;Name&quot;:&quot;Happy Blocks&quot;,&quot;OwnerId&quot;:&quot;0055500000BVUTuAAP&quot;}</sobjectValue>
            </value>
        </parameters>
        <parameters>
            <leftValueReference>$Record</leftValueReference>
            <type>InputTriggeringRecordUpdated</type>
            <value>
                <sobjectValue>{&quot;BillingCountry&quot;:&quot;Japan&quot;,&quot;CleanStatus&quot;:&quot;Pending&quot;,&quot;Name&quot;:&quot;Happy Blocks&quot;,&quot;OwnerId&quot;:&quot;0055500000BVUTuAAP&quot;}</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
--- ---