Your AI agent passed every unit test in the sandbox. It scored 92% code coverage. The deploy pipeline went green. Then it hit production, hallucinated a refund policy, and cost your company a six-figure customer.
Sound familiar? The dirty secret of Agentforce development in 2026 is that most teams are shipping agents with zero real integration tests. Standard Apex unit tests mock every callout, roll back every transaction, and never actually exercise the Agentforce planner or Data 360 query engine. You are testing the plumbing while the AI brain runs unchecked.
Summer ’26 (API v67.0) introduces @IntegrationTest — a new Apex annotation that breaks through the mock barrier and lets your test classes make real callouts to Agentforce and Data 360, commit data mid-transaction, and assert on actual service responses. It is available as a Developer Preview in scratch orgs, and it fundamentally changes how you validate AI-powered Salesforce applications before they reach customers.
This guide walks you through everything you need to know: the annotation syntax, the new commitTestOnly() primitive, real-world test patterns for Agentforce actions and Data 360 queries, best practices, and current limitations.
Why Standard Apex Tests Cannot Validate Agents
For fifteen years, the Apex testing model has been elegantly simple: write a @IsTest method, mock your callouts, assert your logic, and let the framework roll back every database change. Same input, same output, every run. Deterministic.
Agents are not deterministic.
Feed the same utterance to an Agentforce agent twice and you may get two different reasoning paths, two different topic matches, sometimes two different action invocations. The model has built-in variability shaped by memory, prior turns, and tool access. A standard @IsTest class that mocks the agent callout and asserts on a canned response tells you nothing about whether the actual Agentforce planner will route correctly, call the right action, or return a response that your downstream logic can consume.
The numbers are staggering. In Q1 2026, Agentforce customers consumed over 20 trillion tokens — a 400% year-over-year jump — amounting to roughly 1.79 billion agentic work units. Every one of those interactions was a decision your agent made autonomously. A wrong topic selection, a misfired action, or a tone-deaf response multiplied across millions of conversations turns the cost of skipping integration QA from theoretical to very real.
What @IntegrationTest Actually Changes
The @IntegrationTest annotation marks a class and its methods as integration tests that relax two fundamental constraints of standard Apex testing:
1. Real Callouts to Agentforce and Data 360
In standard @IsTest methods, any HTTP callout to Agentforce or Data 360 is blocked. You must implement HttpCalloutMock to return a canned response. Integration tests remove this restriction entirely — your test methods invoke the actual Agentforce planner and query real Data 360 data model objects (DMOs) without mocks.
2. Mid-Transaction Data Commits
Standard tests auto-rollback all DML at the end of the test method. This means Agentforce services, which operate on separate threads, cannot see any data your test created. The new IntegrationTest.commitTestOnly() method lets you commit data to the database at specific checkpoints within your test, making it visible to Agentforce and Data 360 services.
Comparison Table: @IsTest vs @IntegrationTest
| Capability | @IsTest (Unit Test) | @IntegrationTest |
|---|---|---|
| Agentforce/Data 360 callouts | Blocked — require mocks | Allowed — real service calls |
| Transaction behavior | Auto-rollback | Data committed; use @TearDown for cleanup |
| Data visibility | Test data silo by default | SeeAllData=true by default |
| Code coverage | Counts toward deployment requirements | Does NOT count toward deployment coverage |
| Metadata deployments | Executed during deployments | Excluded from RunAllTests |
| Execution mode | Synchronous or asynchronous | Asynchronous only; 1 concurrent test per org |
| Maximum runtime | Standard limits | 10 minutes |
Setting Up Your Scratch Org for Integration Testing
Integration tests currently run only in scratch orgs during the Developer Preview. You cannot run them in sandboxes, production orgs, or during metadata deployments. This is a deliberate constraint — Salesforce wants you to experiment safely before the feature graduates to GA.
Step 1: Enable the Feature in Your Scratch Org Definition
Add ApexIntegrationTests to the features array in your config/project-scratch-def.json:
{
"orgName": "Your Company",
"edition": "Developer",
"features": ["ApexIntegrationTests"]
}
Step 2: Create the Scratch Org
sf org create scratch --definition-file config/project-scratch-def.json --alias int-test-org --set-default
Step 3: Deploy Your Metadata
Push or deploy your source — Apex classes, Agentforce configurations, Prompt Templates, Flows, and any custom objects your agent depends on — into the scratch org before running integration tests.
sf project deploy start --source-dir force-app
Core Primitives: @IntegrationTest, @TearDown, and commitTestOnly()
Three new building blocks form the foundation of every integration test class:
@IntegrationTest (Class and Method Annotation)
When applied to a class, @IntegrationTest marks it as an integration test container. The class can only contain integration test methods and @TearDown methods. You cannot annotate a class with both @IntegrationTest and @IsTest.
Integration test methods can call utility methods from @IsTest classes (like shared test data factories), but @IsTest methods cannot call integration test methods.
@TearDown (Cleanup Annotation)
Marks a static method that runs after the test completes, regardless of pass or fail. This is your cleanup hook. Because integration tests commit real data to the database, failing to tear down test records will pollute subsequent test runs and potentially your scratch org’s data.
Critical rule: Delete child records before parent records to avoid foreign key constraint violations.
IntegrationTest.commitTestOnly() (Mid-Transaction Commit)
This is the most important new primitive. It commits all pending DML to the database, making your test data visible to Agentforce and Data 360 services that operate on separate threads. It also:
- Resets the uncommitted work checkpoint, so subsequent callouts do not fail with the “uncommitted work pending” error
- Resets Mixed DML tracking, so you can perform setup sObject and non-setup sObject DML in separate commit windows
You can call commitTestOnly() multiple times within a single test method for progressive data commits. Calling it outside an @IntegrationTest context throws a runtime error.
Real-World Test Patterns with Code
Pattern 1: Testing an Agentforce Action End-to-End
This is the bread-and-butter pattern. You create test data, commit it, invoke the agent via Invocable.Action, and assert on the response.
@IntegrationTest
public with sharing class AgentforceIntegrationTest {
@IntegrationTest
public static void testAgentSummarizesAccount() {
// 1. Create test data
Account a = new Account(
Name = 'AgentDemoAccount',
AnnualRevenue = 1000000
);
insert as user a;
// 2. Commit so Agentforce can see it
IntegrationTest.commitTestOnly();
// 3. Invoke the agent action
Invocable.Action action = Invocable.Action.createCustomAction(
'generateAiAgentResponse',
'Demo_Action'
);
action.setInvocationParameter(
'userMessage',
'Summarize my Account ' + a.Id
);
List<Invocable.Action.Result> results = action.invoke();
String response = results[0]
.getOutputParameters()
.get('agentResponse')
.toString();
// 4. Assert on the response
Assert.isNotNull(response, 'Agent should return a response');
Assert.isTrue(
response.contains('AgentDemoAccount'),
'Response should reference the account name'
);
}
@TearDown
public static void tearDown() {
delete as user [
SELECT Id FROM Account
WHERE Name = 'AgentDemoAccount'
];
}
}
Why commitTestOnly() before the agent call is critical: The Agentforce planner runs on a separate thread. Without the commit, the Account you created exists only in your pending transaction and is invisible to the planner. The agent would either fail or return a generic response unrelated to your test data.
Pattern 2: Querying Data 360 DMOs Without Mocks
In standard unit tests, SOQL queries against Data 360 data model objects (DMOs) require SoqlStubProvider or Test.createSoqlStub. Integration tests bypass this restriction entirely.
@IntegrationTest
public with sharing class DataCloudQueryIntegrationTest {
@IntegrationTest
public static void testDMOQuery() {
List<SObject> rows = Database.query(
'SELECT Id FROM Account__dlm WITH USER_MODE LIMIT 1'
);
Assert.areEqual(
1,
rows.size(),
'Data 360 query should return 1 row'
);
}
}
This is transformative for teams building on Data 360. You no longer need to maintain brittle mock implementations of your DMO queries — you test against the actual data model.
Pattern 3: Progressive Commits for Complex Workflows
When your test scenario involves multiple dependent operations — create an Account, update it, create a Contact, then invoke an agent — you need multiple commitTestOnly() calls.
@IntegrationTest
public with sharing class MultiCommitIntegrationTest {
@IntegrationTest
public static void testMultipleCommits() {
// First commit: create and persist the account
Account a = new Account(Name = 'Commit Test Account');
insert as user a;
IntegrationTest.commitTestOnly();
// Second commit: update and persist
a.Website = 'example.com';
update as user a;
IntegrationTest.commitTestOnly();
// Third commit: create dependent contact
Contact c = new Contact(
FirstName = 'Test',
LastName = 'Contact',
AccountId = a.Id
);
insert as user c;
IntegrationTest.commitTestOnly();
// Now query — all three commits are visible
Assert.areEqual(1,
[SELECT COUNT() FROM Account
WHERE Name = 'Commit Test Account' WITH USER_MODE]);
Assert.areEqual(1,
[SELECT COUNT() FROM Contact
WHERE AccountId = :a.Id WITH USER_MODE]);
}
@TearDown
public static void tearDown() {
delete as user [SELECT Id FROM Contact WHERE LastName = 'Contact'];
delete as user [SELECT Id FROM Account
WHERE Name = 'Commit Test Account'];
}
}
Pattern 4: Testing Field History Tracking
Standard unit tests roll back all data, so field history records are never created. This has been a blind spot for years. Integration tests commit data, making it possible to assert on field history for the first time.
@IntegrationTest
public with sharing class FieldHistoryIntegrationTest {
@IntegrationTest
public static void testFieldHistoryIsTracked() {
Account a = new Account(
Name = 'HistoryTestAccount',
Website = 'example.com'
);
insert as user a;
IntegrationTest.commitTestOnly();
a.Website = 'salesforce.com';
update as user a;
IntegrationTest.commitTestOnly();
List<AccountHistory> history = [
SELECT Id FROM AccountHistory
WHERE AccountId = :a.Id WITH USER_MODE
];
Assert.isTrue(
history.size() > 0,
'Expected field history to be tracked'
);
}
@TearDown
public static void tearDown() {
delete as user [
SELECT Id FROM Account
WHERE Name = 'HistoryTestAccount'
];
}
}
Pattern 5: Running Tests as a Specific User
Use System.runAs() to validate agent behavior from different user contexts — critical for testing permission-dependent agent actions.
@IntegrationTest
public with sharing class RunAsIntegrationTest {
@IntegrationTest
public static void testAgentResponseAsStandardUser() {
User u = [SELECT Id FROM User
WHERE Alias = 'tstUsr' LIMIT 1];
System.runAs(u) {
Account a = new Account(Name = 'AgentDemoAccount');
insert as user a;
IntegrationTest.commitTestOnly();
Invocable.Action action = Invocable.Action
.createCustomAction(
'generateAiAgentResponse',
'Demo_Action'
);
action.setInvocationParameter(
'userMessage',
'Summarize my Account ' + a.Id
);
List<Invocable.Action.Result> results =
action.invoke();
String response = results[0]
.getOutputParameters()
.get('agentResponse')
.toString();
Assert.isTrue(
response.contains('AgentDemoAccount')
);
}
}
@TearDown
public static void tearDown() {
delete as user [
SELECT Id FROM Account
WHERE Name = 'AgentDemoAccount'
];
}
}
How to Run Integration Tests
Integration tests are classified as a distinct “IntegrationTest” category, separate from Apex unit tests, flow tests, and Agentforce tests. They are excluded from RunAllTests during metadata deployments.
Via Salesforce CLI
# Run a single integration test class synchronously
sf apex run test --class-names AgentforceIntegrationTest --synchronous --result-format human
# Run multiple integration test classes asynchronously
sf apex run test --class-names AgentforceIntegrationTest,DataCloudQueryIntegrationTest --result-format human
Via Tooling API
You can also invoke integration tests through the Tooling API by specifying the IntegrationTest test level.
Best Practices for Production-Grade Integration Tests
- Always implement @TearDown. Because integration tests commit real data, test records that are not cleaned up will pollute subsequent runs. Delete child records before parent records.
- Call commitTestOnly() before every Agentforce or Data 360 callout. These services run on separate threads and cannot see uncommitted data in your test’s pending transaction.
- Keep integration tests focused on integration concerns. Do not use
@IntegrationTestto test isolated business logic. That is what@IsTestis for. Reserve integration tests for scenarios that genuinely require real service interactions. - Create setup data; avoid reusing org data. Because
SeeAllData=trueis the default, you have access to all org data. But relying on existing data creates row lock contention and makes tests brittle. Create your own test data and tear it down. - Plan for the 10-minute runtime limit. If your test involves multiple slow Agentforce calls, split into separate test methods rather than one monolithic test.
- Mock external HTTP endpoints. Even in integration tests, regular HTTP callouts to non-Salesforce endpoints still require
HttpCalloutMock. Only Agentforce and Data 360 services are exempt. - Design for concurrency. Only one integration test can run per org at a time. Prefer shorter, focused tests over long suites that block your CI pipeline.
- Assert on routing, not exact text. Agent responses are non-deterministic. An assertion like
response.contains('AgentDemoAccount')is robust. An assertion that checks for an exact sentence will fail on the next run.
Current Limitations (Developer Preview)
As of Summer ’26, @IntegrationTest is in Developer Preview. Be aware of these constraints:
- Scratch orgs only. You cannot run integration tests in sandboxes, production orgs, or during metadata deployments.
- No code coverage contribution. Integration test execution does not count toward deployment code coverage requirements.
- No @TestVisible access. Unlike
@IsTestclasses,@IntegrationTestclasses cannot access private members annotated with@TestVisible. - Asynchronous execution only. Tests run one at a time per org, sharing the 24-hour async test run limit with flow tests and unit tests.
- Standard governor limits apply. Asynchronous Apex limits for SOQL, DML, CPU, and heap are enforced.
The Bigger Picture: Agent Testing as a Discipline
@IntegrationTest is one piece of a larger Summer ’26 testing story. Salesforce also shipped:
- Agent Preview (GA) — script interactive test sessions from the Salesforce CLI with
sf agent preview start, capture trace files showing how your agent routed and acted - Testing Center in Agentforce Studio — batch tests, conversation-level simulation with user personas, moved from Setup into Studio
- Custom Scorers (Beta) — grade agent sessions against your own KPIs (sentiment, tone of voice, escalation trigger, product interest) using natural-language rubrics
Together, these tools form a complete agent testing lifecycle: build in scratch orgs with @IntegrationTest, script conversations with Agent Preview, batch test in Testing Center, and grade quality with Custom Scorers.
The pattern is clear: Salesforce is past the “launch hype” phase of Agentforce and into the “enterprise reliability” phase. The platform now treats agent testing as a first-class discipline with its own surface, its own primitives, and its own place in your deployment pipeline.
Conclusion: Ship Agents You Can Actually Trust
The @IntegrationTest annotation closes the biggest testing gap in Agentforce development. For the first time, you can write Apex tests that exercise real Agentforce callouts, query real Data 360 DMOs, commit data mid-transaction, and assert on actual service responses — all within your existing Apex test culture.
Yes, it is a Developer Preview limited to scratch orgs. Yes, integration tests do not count toward code coverage. But the capability is real, the code patterns are production-ready, and the feature will almost certainly graduate to GA in a future release.
Your action item this week: Add ApexIntegrationTests to a scratch org definition, pick your most critical Agentforce action, and write your first @IntegrationTest class. Commit the test data, invoke the agent, and read the actual response. That one exercise will teach you more about your agent’s real behavior than any amount of manual testing in a chat window.
The era of shipping untested AI agents is over. Summer ’26 gave you the tools. Now use them.
