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 calloutsBlocked — require mocksAllowed — real service calls
Transaction behaviorAuto-rollbackData committed; use @TearDown for cleanup
Data visibilityTest data silo by defaultSeeAllData=true by default
Code coverageCounts toward deployment requirementsDoes NOT count toward deployment coverage
Metadata deploymentsExecuted during deploymentsExcluded from RunAllTests
Execution modeSynchronous or asynchronousAsynchronous only; 1 concurrent test per org
Maximum runtimeStandard limits10 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

  1. 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.
  2. 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.
  3. Keep integration tests focused on integration concerns. Do not use @IntegrationTest to test isolated business logic. That is what @IsTest is for. Reserve integration tests for scenarios that genuinely require real service interactions.
  4. Create setup data; avoid reusing org data. Because SeeAllData=true is 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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 @IsTest classes, @IntegrationTest classes 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.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *