| Tweet |
|
| Image created by ChatGPT5.2 based on a prompt by Bob Buzzard |
Introduction
In my first post on this new functionality I did a bit of digging into which tests are chosen when code is changed. If leaving it up to Salesforce doesn't quite cut it, there's the option to influence things via the two new parameters for the @isTest annotation.
As in the first post, I've got a small set of classes with dependencies that
are sometimes static and sometimes dynamic. As a refresher the key
classes are:
-
OpportunityUtils
The protagonist in my little drama is a class named This implements an interface (OpportunityUtilsIF) with a single method, getBigDeals(), which receives a collection of opportunities and returns a new collection containing just those opportunities with a value greater than or equal to 250,000.
There is a dedicated test class (OpportunityUtilsTest) which directly instantiates the class and executes a zero/one test.
-
OpportunityEOD
This contains a single method (EODProcessing) that extracts all opportunities created today, creates an instance of OpportunityUtils, extracts just the big deals, appends ' - BIG DEAL' to their name and updates them.
There is a dedicated test class (OpportunityEODTest) that inserts test opportunities of varying values, instantiates OpportunityEOD and executes the EODProcessing method, then extracts all opportunities from the database that are greater than or equal to 250,000 and asserts each name contains ' - BIG DEAL'.
-
OpportunityWrapLevel1
This contains a single method (EODProcessingWrapLevel1) that instantiates the OpportunityEOD class and executes the EODProcessing method.
There is a dedicated test class (OpportunityWrapLevel1Test) that inserts test opportunities of varying levels, instantiates OpportunityEODWrapLevel1, executes the EODProcessingWrapLevel1 method, then extracts all opportunities from the database that are greater than or equal to 250,000 and asserts each name contains ' - BIG DEAL'.
-
OpportunityEODInjection
This contains a replica of the EODProcessing() method, but rather than directly instantiating OpportunityUtils it is passed a parameter (implementing the OpportunityUtilsIF interface. There is a dedicated test class (OpportunityEODInjectionTest) that delegates to a test factory to dynamically create an instance of OpportunityUtils based on the class name - at no point is OpportunityUtils directly referred to. The test mirrors the other EOD tests, inserting opportunities, carrying out the EOD processing and verifying that ' - BIG DEAL' is appended where appropriate.
As we are now in the scratch org preview window, I was able to use my pre-release developer edition as a Dev Hub and create a Spring '26 scratch org, which speeds things up enormously and lets me scrap everything and start again from scratch with minimum effort.
@IsTest(critical=true)
This parameter tells the Salesforce test engine that the tests in this class
must execute when a deployment takes place. I originally misread the docs on
this and thought the test only executed if the payload contained Apex changes,
but that isn't the case. Setting a test class as critical means it always
executes for a deployment that runs relevant tests regardless of what is being
deployed. To confirm this, I returned to the classic example of a
configuration change that breaks tests with ease - the validation rule!
I marked my OpportunityEODTest class as critical, attempted to deploy an Opportunity validation rule that required one of Description or Lead Source to be populated, and duly watched the test execute and the deployment fail!
I marked my OpportunityEODTest class as critical, attempted to deploy an Opportunity validation rule that required one of Description or Lead Source to be populated, and duly watched the test execute and the deployment fail!
This is a great addition, as the critical tests are identified without the
deployer having to remember which tests they should always run. That said the
deployer does still have to remember to deploy with the
RunRelevantTests option, so it's
not a silver bullet.
@IsTest(testFor='<classes_and_triggers>')
This parameter comes in handy if the Salesforce test engine isn't picking up
all the tests that matter. In my first post I explained how it isn't really
reasonable to expect the test engine to pick up dynamic instantiation of
the OpportunityUtils class based on its name, which is how it's used in OpportunityEODInjection. Using the testFor parameter I
can give the test engine the helping hand it needs:
@isTest(testFor='ApexClass:OpportunityUtils')
private class OpportunityEODInjectionTest {
@isTest
static void TestEODProcessing() {
...
}
}
Note that I don't have to include OpportunityEODInjection in the list of classes identified in the testFor
parameter. I'm adding to the tests that are executed, not
overriding them. This test is also executed if I change the OpportunityEODInjection class, as it has a direct dependency on it which the Salesforce test
engine can pick up.
Even though that is the case, I think in a real-world environment I'd prefer
to list all the classes/triggers that the test class is associated with, as
it improves clarity and saves a developer having to figure things out
manually, even if there is overhead to create and maintain this
information.
More Information

No comments:
Post a Comment