Advanced Salesforce Developer, Simon Lawrence, tackles the issue of declarative testing tools.ย
With the dawn of Winter โ19, we saw what might be the first shift in the direction of testing tools, requirements and capabilities for the Declarative tools.
(Iโll now wait for the sound of the Administrator Stampede to subside)
Winter โ19 saw the exposure of the โFlowTestCoverageโ object via the Tooling APIโฆ which told us all the actually Salesforce are clocking the Test Coverage of declarative Processes and Flows.. Who knew it?!
As of now, it is just the result of Apex tests and how they cover these business processes; but if I poke my neck out of the nice safe harbour, it feels like this might be the first step towards provisioning a declarative testing tool – and maybe even slowly introducing a requirement for finally having tests to assert the behaviour of these unbelievably powerful entities (though as it seems like Salesforce are winding down on required tests for even code, perhaps these will never be mandatory).
So to get ahead of the game, I thought I would look quickly at how one might go ahead getting coverage of an auto-invoked Flow, fired from a Process Builder.
We are still stuck with code to do this for now (October 2018) and so the experience is not too different to testing other event based technologies like Triggers.
So letโs look at something we want to test:
Our scenario is that we have a Process Builder that fires when a specific field is updated on an Opportunity, which populates a โOpportunity Valueโ on a child โTracking Recordโ. We will assume that some other automation creates the default Tracking Record when an Opportunity is first saved.
So our process might look like this:
Which invokes a flow (with the opportunity Id as an input parameter) that looks like this:
How do we test this then? Well, we need to cause our Process Builders event to happen, which is to โupdateโ an Opportunity appropriately, and then check that the last Tracking Record has been updated. We could all do this through the browser, but how do we make the System check it on our behalf?
Letโs write some code! Fire up the Developer Console, or your favourite IDE, and let’s get testing.
First off we need to make a new Apex Class, annotated as being a test; it is a good convention to name the test after the unit it is testing (in this case the โOpportunityTrackingโ process and flow). Then, for this test, we are going to need an Opportunity, so we create and insert one of these in a โtestSetupโ method. This will look something like this:
@isTest
public class OpportunityTrackingProcessBuilderTest {
@testSetup
private static void InsertTestOpp() {
// Construct a test Opportunity
Opportunity opp = new Opportunity(Name = 'test opportunity',
StageName = 'Prospecting',
Tracked_Value__c = 1,
CloseDate = Date.today());
// Save this Opportunity to the database
insert opp;
}
// Our test methods now go here
}
If we were to run this test class now, it would execute, and it will already โcoverโ some stuff, as it is going to insert an Opportunity, which will fire that Process to create the default tracking record (for example), but what we need to do is write some code to fire our event – which is to change the Tracked_Value__c amount.
So we write a @testMethod, in the same class (as mentioned in the last comment) to load, change and save this Opportunity. That should then cause the Process builder to fire – just like if we did it through the browser:
@isTest
private static void UpdateOpportunity_ChangeTracked_ProcessFired() {
// Load the Opportunity:
Opportunity loadedOpp = [SELECT Tracked_Value__c
FROM Opportunity];
// Set the Tracked value to something else:
loadedOpp.Tracked_Value__c = 5;
// Save it to the database, and fire the process builder
update loadedOpp;
}
So, there we go! In essence, our test class – when executed – now will insert an Opportunity, update it and save it again, firing our Process Builder. If we run this test and the whole of California doesnโt burst into flames, we can probably suppose that our Process Builder and Flow was ok.
However, supposing stuff isnโt very reliable. What we want to do is assert it. So we would now add a couple of checks to the bottom of our test method to make sure the output we were expecting actually happened. In this case, we will want to load the Opportunityโs tracking record and check itโs Opportunity Value is now the new โ5โ. To do that, we add some more code to our test.
@isTest
private static void UpdateOpportunity_ChangeTracked_ProcessFired() {
// Load the Opportunity:
Opportunity loadedOpp = [SELECT Tracked_Value__c
FROM Opportunity];
// Set the Tracked value to something else:
loadedOpp.Tracked_Value__c = 5;
// Save it to the database, and fire the process builder
test.startTest();
update loadedOpp;
test.stopTest();
// Load the tracking record for this Opportunity
Tracking_Record__c trackingResult = [SELECT Opportunity_Value__c
FROM Tracking_Record__c
WHERE Id = :loadedOpp.Id];
// Check the tracked value is now 5
System.assertEquals(5, trackingResult.Opportunity_Value__c);
}
This test actually is now fundamentally complete. It would be a fairly acceptable piece of work from a new developer and should reliably assert that the behavior of this Process and Flow are working. If someone changed the Flow or Process you may well find this test starts failing, which could indicate to you that this had happened and you might want to question why.
Now then, what other scenarios might we want to test here? It is not a very rigid test, and only tests the โcorrectโ case. You can write many test methods in the same class (reusing a fresh version of the test Opportunity each time).
Which of the following scenarios might you also want to write a test method for to make sure nothing goes wrong in your Orgโฆ? Probably not all of them, but they are all food for thought!
Our independent tech team has been servicing enterprise clients for over 15 years from our HQ in Bristol, UK. Let’s see how we can work together and get the most out of your Salesforce implementation.