Tuesday, 27 October 2026

Latest comes with Strings Attached in Winter '27


Image generated by ChatGPT 5.6 Sol based on a prompt by Bob Buzzard

Introduction

The Winter '27 Salesforce release includes a developer quality-of-life improvement, the option to always target the latest supported version of the REST API by using the alias latest in place of the explicit version number. For example, if I want to hit the latest version in my developer edition, rather than specifying API version 68.0 for Winter '27:

    https://kabtutorial-dev-ed.my.salesforce.com/services/data/v68.0/sobjects/Account

I can use latest:

    https://kabtutorial-dev-ed.my.salesforce.com/services/data/latest/sobjects/Account

No more updating the REST API versions of my integrations every 4 months just to keep them on the latest version, and I can stop worrying about API version retirement as I'll always be on the latest and greatest. What's not to like?

Convenience versus Control

There's no argument that this is more convenient. My integrations automatically use whatever Salesforce considers current, no action required. This also represents a loss of control though, as my integrations automatically use whatever Salesforce considers current. Even if that causes problems. 

My code remains the same, but a dependency changes underneath it, for good or bad. In a few months time latest will mean v69.0 whether I'm ready for it or not. This doesn't mean that I'd expect failure, but new API versions can introduce behavioural differences, new fields, updated metadata, which an integration may not be able to handle. The key point is that I've surrendered control over when I consume the new version.

When I use a fixed API version, upgrading is a planned event with testing. When I use latest, the upgrade comes as part of the release and the effort moves to my release preview testing. The responsibility to test still lies with me, all I've handed off is the decision to upgrade. 

When latest is greatest

Salesforce's recommendation is to use a fixed API version for production integrations dependent on a specific contract, while scripts and internal tools can work with latest. If this sounds like common sense, that's because it is. If your integration depends on something not changing, then don't allow that dependency to change! 

Fixed is the safe option?

Fixed certainly gives more control, but it can't really be described as truly safe. Once API v68.0 (Winter '27) went live, it actually impacted all earlier API versions. If you have an integration fixed at API v67.0 (Summer '26), Salesforce doesn't spin up a frozen-in-time Summer '26 release for you to work against. You hit the current, Winter '27, version of the platform which implements backward compatibility to preserve the contracted behaviour of API v67.0/Summer '26. So even if your REST API version doesn't change, the Salesforce platform you depend on does, and you should still regression test accordingly. 

Of course not many of us formally carry out this regression testing in preview sandboxes every release. Some of that is because Salesforce carry out extensive testing themselves, and they've got a good track record so we just trust them to get it right. Most of it, in my view anyway, is complacency! Salesforce releases don't feel like that kind of change - they are more like infrastructure maintenance than an application upgrade. Plus regression testing is expensive, and if nothing broke in the last 10 years it's easy to get lulled into a false sense of security?

Conclusion

So to sum up, feel free to use latest for things that aren't critical, but it's best to stick with fixed API versions for things that are critical. And if those things really are critical, make sure you regression test them every release regardless of which version they target.

More Information

Tuesday, 20 October 2026

Elastic Limits for Async Apex Jobs in Winter '27


Image generated by ChatGPT 5.6 Sol based on a prompt from Bob Buzzard

Introduction

The Summer '26 Salesforce release introduced the beta of elastic limits for asynchronous Apex Queueable and Future methods. It stays in beta in Winter '27, but extends the capability to Batch jobs. 

If you aren't familiar with this feature, it is essentially a safety net for when your asynchronous processing takes a hot turn and you suddenly find your system executing more jobs than governor limits allow. 

What's in a number?

Without elastic limits, as soon as you exceed the standard limit of

   max(250k, 200*applicable user licenses)

attempts to enqueue* additional jobs fail. 

With elastic limits you can continue to enqueue* jobs up to the additional elastic limit of

   min(2m, standard limit)

This isn't simply a doubling of the limit though - while that would be very cool for us, it would likely send a few Salesforce data centres into meltdown! Additional jobs enqueued* over the standard limit are subject to slowdown. Active job processing is throttled, and Batch jobs are limited to one active job at a time. 

How slow is slow?

Salesforce doesn't publish details of the throttling, which doesn't surprise me. This feature should be thought of as a safety net that makes use of spare capacity to help you avoid losing work. If the throttling algorithm were to be published, I'm pretty sure it would be widely misinterpreted as an SLA, with capacity planning based on behaviour that isn't contractual. 

The main thing to remember is that capacity is guaranteed, obviously up to the elastic limit, but throughput isn't. You can continue to enqueue* jobs, but you can't predict when they will execute, or when they will complete their processing. To be fair, if we take the Salesforce docs at their word, then this is no different to regular asynchronous processing:

Because many customers share available resources on each Salesforce instance, the exact execution timing of asynchronous requests cannot be predicted or guaranteed.

For the vast majority of scenarios this is fine. My experience is there are rarely delays (outside of service issues), and processing completing slightly later than expected typically has little to no business impact. If processing is really time-sensitive and mission-critical, we all know it should happen outside of Salesforce on infrastructure with guarantees. 

How can I tell I've hit the elastic?

There's a couple of ways to achieve this. 

The first is through the time-honoured mechanism of looking at a web page, the Apex Jobs setup page in this case:


The second, and probably more useful for developers, is using the OrgLimit class:

   Map<String,System.OrgLimit> limitsMap = OrgLimits.getMap();

// -- Standard Limits -- System.OrgLimit asyncApexDailyLimit = limitsMap.get('DailyAsyncApexExecutions'); // Total enqueued in last 24 hours Integer usedLimit=asyncApexDailyLimit.getValue(); // Daily limit Integer dailyLimit=asyncApexDailyLimit.getLimit(); // -- Elastic Limits -- System.OrgLimit asyncApexElasticLimit = limitsMap.get('DailyAsyncApexElasticExecutions'); // Total enqueued in last 24 hours - same as asyncApexDailyLimit.getValue() Integer elasticUsedLimit=asyncApexElasticLimit.getValue(); // Elastic limit Integer elasticDailyLimit=asyncApexElasticLimit.getLimit();

There's a couple of things to bear in mind here though:

  1. If you haven't enabled elastic limits, executing   
       limitsMap.get('DailyAsyncApexElasticExecutions')
    will return null

  2. For non-production orgs the standard + elastic limit is the same as the standard limit - only production orgs have additional capacity

No elastic in the schedule?

The eagle-eyed reader will have noticed that scheduled Apex is conspicuous by its absence. While the timeline so far might suggest it is coming in the next release, my own view is that this is rightly excluded. The elastic limit swaps timeliness for additional capacity - the benefit is the processing will complete eventually. Scheduled Apex requires timeliness - typically we schedule jobs to run at a particular time because that is when we need the processing to happen. 

Of course there are times when scheduled jobs just have to run at some point during a day, but to schedule them you have to choose a specific time. These jobs are candidates for elastic limits, but the problem is there's no way that Salesforce can know this. Luckily these jobs can just hand off to Queueable, Batch, or even Future if you are feeling retro, and take full advantage of the elastic capacity (such as it is).

*Enqueued or executed?

That's a great question, and one that I don't really have an answer to. The Salesforce docs seem to switch between executing and enqueuing jobs, and the Apex Jobs page throws processed into the mix, so I've continued with that. I assume that you won't be able to enqueue infinite jobs and there will only be exceptions when the executions exceed the elastic limit, but I'm not seeing that clearly called out in the docs. Remember that this is a beta feature - I'm sure by the time it becomes generally available the docs will have tightened up a little.

Conclusion

The main takeaway is that two times the asynchronous allowance doesn't mean two times the compute, or anything like it. This is a nice gesture from Salesforce that can really help with resilience, but if you are regularly exceeding the standard limit then it's worth investigating why and what you can do about it, even if that means offloading some processing elsewhere.

More Information


 

Tuesday, 8 September 2026

Increased Apex Heap Size in Winter '27


Image generated by ChatGPT 5.6 based on a prompt by Bob Buzzard

Introduction

The Winter '27 release of Salesforce gave us an unexpected uplift in the Apex heap limit. Synchronous Apex gets 10Mb, an increase of 4Mb or 66% over the previous 6Mb, while asynchronous Apex gets a whopping 25Mb, an increase of 13Mb or 108% over the previous 12Mb. Given that all the memory in the world is currently being consumed by AI data centres, this is not something I was expected.

There's also a really nice touch in Apex Settings while we're in the release transition period - we can ask non-production Winter '27 orgs to apply the Summer '26 heap limit, so we don't inadvertently release code that relies on a 10Mb heap limit to orgs that max out at 6Mb. 

I've previously messed with heap limits when trying out Zip Handling in Apex back in February 2025. I was able to briefly push the 6Mb limit to 25Mb when processing a large zip file, so I was keen to see what could be achieved with 10Mb.

After spinning up a Winter '27 preview scratch org and deploying the Zip Handler sample code, I set about pushing the limit. A zip file of 28Mb presented no problem, but also didn't push the heap much further than my original tests in Spring '25. The non-linear nature of compression algorithms I guess. A zip file of 100Mb blew up pretty much immediately when retrieving the file body. Much as I expected, but it would have been cool if it had worked. Splitting the difference to 75Mb suffered the same fate.

Cutting the zip file down to 51Mb hit the sweet spot, as my LWC confirmed:

    51,810,068 byte zip file consumed 201 msec of cpu and 50,783,500 bytes of heap

The CPU is low because there are only a couple of files in there, and they are MOV files which are already compressed. The heap size is impressive though - Apex has (briefly) allowed me to consume 5 times the documented limit. Obviously relying on this in any production scenario would likely be a career-limiting choice - it's all fun and games until the runtime notices, kills my transaction, and leaves me with a file that I've accepted but can't actually process.

Bigger isn't Always Better

The increased heap limit is, of course, very welcome. That said, there's a couple of things I think it's important to bear in mind:

  • Heap isn't throughput. The gating factor on your automated process may have been heap, but just because you have more heap doesn't mean you can process as many records as you can stuff into a transaction. The other limits remain in place, and retrieving more records to process will likely push your CPU consumption higher. Code that scales poorly will simply have more room to consume resources before it fails, so it's important to understand how your code behaves as the inputs grow. (Growth Testing is something I cover in my new book, Software Testing on the Salesforce Platform).
  • Just because you can retrieve tons of data, doesn't mean you should deliver it. A larger heap means more information can be sent back to the user, but always consider the user experience. Thousands of records dumped in a table that a user has to scroll through can quickly get overwhelming, and they'll be treated to a longer wait while all that information makes its way through the layers. Sending a limited set of results back to the user with the ability to filter is often better received than sending everything because nothing was stopping you. Similarly, increased volumes of data returned from an API call might overwhelm a downstream system, 
A more nuanced consideration is that you might see issues as code silently becomes more capable. I'm sure there are business processes out that that always fail if more than 'x' records are selected for processing. Everyone has got used to this, and maybe in the odd case it's a key attribute of the automation. All of a sudden this code doesn't fail until it gets '2x' records to process, but the increased volumes it can handle cause issues elsewhere. Beware historical assumptions on when transactions would be terminated automatically.

Yesterday's Best Practice is Still the Right Approach

Writing Apex code to conserve limits and break large workloads into separate transactions is still the way to do it. Just because there is more heap to work with doesn't mean we can start retrieving all fields regardless of whether we need them, or leave copies of everything hanging around. The increased heap limit allows us to handle legitimately larger workloads, but we shouldn't view it as a target to be hit on every transaction, or stop caring about heap consumption.

More Information


 

 

Wednesday, 2 September 2026

Apex Integration Testing in Winter '27


Image created by ChatGPT 5.6 based on a prompt from Bob Buzzard

Introduction

Apex integration testing was launched in Developer Preview in the Summer '26 Salesforce release. In my blog post on this new functionality, I noted that it was:

limited to interactions with the Data 360 and Agentforce Salesforce services, but hopefully this will be relaxed in the future and we'll be able to make callouts to any authorised endpoint that we choose. 

In Winter '27 the future is here! While the feature is still in Developer Preview, your tests can now make HTTP callouts to any authorised endpoint, including external services configured via named credentials.

Sample


The sample code (available at this Github repo) needs to be executed against a Developer scratch org running the Winter '27 release (in preview at the time of writing) with the ApexIntegrationTests feature, which you can specify by adding these couple of lines to your project-scratch-def.json file:
   "release": "preview",
   "features": ["EnableSetPasswordInApi", "ApexIntegrationTests", "Einstein1AIPlatform"],

I've used my standard endpoint for proof of concept work - the free tier of Agify (agify.io), which estimates ages based on names. This (currently) doesn't require authentication, works off a simple GET request with the name as a parameter, and returns a small, easily understood JSON response. There's a very simple Agify service, consisting of a getAge() method that sends my name off for an estimate.

The integration test simply executes the service's getAge() method and verifies that the response contains the supplied name. There's not much else worth asserting here - I can't be sure what age will be returned, as the response can change in line with the underlying Agify dataset:

@IntegrationTest
public with sharing class AgifyServiceIntegrationTest {
    @IntegrationTest
    public static void testServiceInteraction() {
        AgifyService.AgifyResponse resp=AgifyService.getAge('Keir Bowden');
        Assert.areEqual('Keir Bowden', resp.name, 
            'The name returned from the service should match the input name.');
    }
}

The key aspect is that the test completes successfully. Running this against the Summer '26 release gives the following error:

12:41:27:012 FATAL_ERROR System.TypeException: Methods defined as TestMethod do not support Web service callouts

(Clearly it wasn't worth defining a bespoke error message for the single release that integration testing didn't allow callouts, but I must defend myself here - I have no "Methods defined as TestMethod", just methods decorated with @IntegrationTest).

I'm really pleased that it's only taken one release to open up integration testing past Agentforce and Data 360. My next hope is that integration testing is never allowed in a production environment - it's just too dangerous in my opinion, especially now there can be callouts to all manner of other production systems. Scratch orgs and sandboxes will be fine for the vast majority of us, and those that really do need to run it all in production can do so outside of a testing framework.

More Information


Follow on LinkedIn


 

Monday, 10 August 2026

Transactions in Apex Integration Testing

Image generated by OpenAI GPT-5.5 from a prompt by Bob Buzzard

Introduction

In the first post in this series I covered a brief overview of the new Apex Integration Testing that is available in Developer Preview in Summer '26. In this instalment I'll take a deeper look at the how transactions work in this new testing paradigm. If you want to play along at home, you can find code samples to verify the behaviour at this Github repository. It might seem a little odd that I'm asserting behaviour rather than simply logging it etc, but I find this to be a useful approach when the implementation is likely to change. Come Winter '27 I'll just execute my tests again and if any fail I'll know something has changed. Tests as a tripwire FTW!

As always, please bear in mind that this functionality is in Developer Preview and the implementation could well change, rendering any findings in this post null and void. It's good fun to figure this stuff out though, so let's have at it.

Integration Tests Execute in a Single Transaction

This is probably the key takeaway of the current implementation. Just because records can be persisted to the Salesforce database as part of an integration test, the transaction doesn't end at that point. The docs for the new IntegrationTest.commitTestOnly() method make this abundantly clear:

Commits data to the database mid-transaction so it’s visible to service threads such as Agentforce and Data 360. It resets the uncommitted work checkpoint and mixed DML tracking for the new transaction boundary. 

The key word(s) here being mid-transaction - we are only part way through, and the transaction will continue for the rest of the test. Sadly this also means that DML limits are not reset by the mid-transaction commit, so we still have to squeeze all of our testing into a single limits context, although like unit tests we do still have the new limits context available through Test.startTest() once we've set up all our data. (See LimitUsageIntegrationTest.cls)

The other interesting part of the docs is the resetting of mixed DML tracking - I foresee that once this is GA we'll all be moving unit tests that have to workaround this particular issue over to integration tests. 

SavePoints Do Not Survive Mid-Transaction Commits

(Thanks to Steve Fouracre for the question about SavePoints - make sure to sign up for our AI events series).

As the transaction continues after a mid-point commit, you could be forgiven for thinking that any SavePoints created earlier in the transaction are still available for use, but sadly (again) that isn't the case. 

Attempting to use a SavePoint (rolling back or releasing) after a commit gives the following error:

18:34:22:291 EXCEPTION_THROWN [15]|System.TypeException:
                                                              Savepoint does not exist in this context

(See SavePointIntegrationTest.cls)

Integration Test Transactions Autocommit

If you make changes to the database but don't carry out a mid-transaction commit, those changes will be auto-committed when the test completes. In this respect an integration test is just like any other Salesforce request. You can see proof of this in AutoCommitIntegrationTest.cls, and note that I'm verifying this through an assertion in the @TearDown method:

@TearDown
static void tearDown() {    
    List<Account> accs=[SELECT Id, Name 
                        FROM Account
                        WHERE Name LIKE 'Integration Test Account%'
                        WITH USER_MODE]; Assert.areEqual(2, accs.size(),
               'Expected to find mid-transaction and auto committed test accounts'); delete as user accs; }

while certainly unusual, there's nothing prohibited about using asserts outside of test methods, and in this case it's the only way I can be sure I'm outside of the integration test transaction boundary. I'd steer clear of it in production tests though, as it will likely cause confusion.

TearDown Executes in a New Transaction

When the @TearDown method executes, it does so in a new transaction context and thus has a full set of governor limits to use to cleanup data. The teardown method autocommits, so as long as it completes successfully your cleanup changes will be persisted. It's worth point out again that teardown methods can error, so you need to use defensive coding to ensure you expect the unexpected. 

Talking of unexpected, it also appears that if you have multiple @TearDown methods, they each get their own transaction In TearDownTestTXIntegrationTest.cls I have two of these and in each I assert that the DML statements consumed is 0, which succeeds even though one of them has to have already executed and handled the delete:

@TearDown
static void tearDown() {
    Assert.areEqual(0, Limits.getDMLStatements(), 'Should be 0 DML statements consumed in teardown');
    delete as user [SELECT Id FROM Account WHERE Name = 'Integration Test Account' WITH USER_MODE];
}

@TearDown
static void tearDown2() {
    Assert.areEqual(0, Limits.getDMLStatements(), 'Should be 0 DML statements consumed in teardown');
    delete as user [SELECT Id FROM Account WHERE Name = 'Integration Test Account' WITH USER_MODE];
}

More Information


Follow on LinkedIn


Wednesday, 29 July 2026

Apex Integration Testing in Summer '26

Image eventually created by OpenAI GPT-5.5 after
about 5 prompts by Bob Buzzard

Introduction

A feature that seems to have flown under the radar in the Summer '26 release of Salesforce is the new Apex Integration Testing capability. For the first time integration testing (albeit quite limited) is a first-class discipline on the platform. With a bit of luck this foreshadows an end to our reliance on external tooling and Heath Robinson (or Rube Goldberg for our friends across the pond) combinations of unit tests and mocks masquerading as integration tests. 

Before we get too excited it's worth bearing in mind that this is currently in Developer Preview for scratch orgs only, and is limited to interactions with the Data 360 and Agentforce Salesforce services, but hopefully this will be relaxed in the future and we'll be able to make callouts to any authorised endpoint that we choose. 

If you'd like to know more about Apex integration testing, I've covered it in depth (as much as possible for a developer preview feature, obviously) in my new book: Software Testing on the Salesforce Platform. 

Integration Tests

If you haven't come across integration testing before, you can think of it as the next programmatic testing step after unit testing. Unit tests check whether a piece of code works in total isolation, while integration tests check that the various unit tested pieces still work correctly when they are connected to each other. 

An Apex class/method becomes an integration test through the simple application of the @IntegrationTest annotation where a unit test would have the @IsTest annotation. Aside from that, they start out rather similar to Apex unit tests: 

  • Set up data 
  • Execute code under test
  • Verify results.
Where they diverge (but align with just about every other unit test framework in the world) is tearing down the changes made. In Salesforce world we've never had to care about that before, as the Apex unit test framework rolls back any changes we've made automatically. Integration tests commit changes to the Salesforce database, so there's clearly a need to reset the database to a known, stable state. 

Teardown methods are identified through the @TearDown annotation, are executed after every test regardless of success or failure, and commit automatically. You can also have more than one of them in a class, in which case they will all execute after every test and you can't specify the order that they execute in. 

It's also really important to bear in mind that just because teardown methods run after every test, they won't necessarily complete successfully. They can error, just like any other piece of code. If they do error, the test will be marked as a failure, as well as the database being left in an indeterminate state. For this reason, defensive code is a must for teardown methods. 

Executing Integration Tests

As integration testing is in developer preview, it can only be used in scratch orgs at present. Enable integration testing through the ApexIntegrationTests feature:


Once you have created a scratch org with this configuration, you can deploy integration test classes to it. You can execute integration test classes like unit test classes, through setup, the developer console or the Salesforce CLI, although remember they have to run asynchronously.

You can only have a single integration test running in an org at any point in time. This isn't an issue in a scratch org, but could be entertaining in sandboxes - everyone gets a couple of hours a week to run tests in peace maybe? Although I'm sure that restriction will be loosened as the functionality approaches GA. 

Apex governor limits apply to integration tests, which probably restricts how useful they will actually be. This is another aspect I wouldn't be surprised to see loosened over time, they are running asynchronously after all, but it's unlikely we'll get to parity with external tools that can span multiple transactions. 

More Information

Follow on LinkedIn


Monday, 19 January 2026

Run Relevant Tests Annotations in Spring '26

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! 

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


Follow on LinkedIn