Mixed DML Operations in Test Methods

Test methods allow for performing mixed DML operations between the sObjects listed in sObjects That Cannot Be Used Together in DML Operations and other sObjects if the code that performs the DML operations is enclosed within System.runAs method blocks. This enables you, for example, to create a user with a role and other sObjects in the same test.

Example: Mixed DML Operations in System.runAs Blocks

This example shows how to enclose mixed DML operations within System.runAs blocks to avoid the mixed DML error. The System.runAs block runs in the current user’s context. It creates a test user with a role and a test account, which is a mixed DML operation.
@isTest
private class MixedDML {
    static testMethod void mixedDMLExample() {  
        User u;
        Account a;
        User thisUser = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId()];
       // Insert account as current user
        System.runAs (thisUser) {
            Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];
            UserRole r = [SELECT Id FROM UserRole WHERE Name='COO'];
            u = new User(alias = 'jsmith', email='jsmith@acme.com', 
                emailencodingkey='UTF-8', lastname='Smith', 
                languagelocalekey='en_US', 
                localesidkey='en_US', profileid = p.Id, userroleid = r.Id,
                timezonesidkey='America/Los_Angeles', 
                username='jsmith@acme.com');
            insert u;
            a = new Account(name='Acme');
            insert a;
        }
    }
}

Using Test.startTest and Test.stopTest to bypass the mixed DML error in a Test Method

The mixed DML exception error is still sometimes returned even if you enclose the code block that performs the mixed DML operations within a System.runAs block. This can occur if the test method calls a future method that performs a DML operation that can’t be mixed with others, such as deleting a group. If you get the mixed DML exception in this case, enclose the code block that makes the future method call within Test.startTest and Test.stopTest statements.

This example shows how enclosing the delete statement between Test.startTest and Test.stopTest statements prevents the mixed DML exception error in a test. Because the delete statement causes a mixed DML operation to be executed by a future method, it is enclosed within the Test.startTest and Test.stopTest statements. The delete statement causes a trigger to fire, deleting the group that was inserted earlier by the account insert trigger by calling the deleteGroup future method of the Util class.

This is the test class that causes a mixed DML operation to occur. The account insertion and deletion fire the triggers.

@isTest
private class RunasTest {
    static testMethod void mixeddmltest() {
        // Create the account and group. 
        Account ac = new Account(Name='TEST ACCOUNT');
        // Group is created in the insert trigger.
        insert ac;
        // Set up user
        User u1 = [SELECT Id FROM User WHERE UserName='testadmin@acme.com'];
        System.RunAs(u1){
            // Add startTest and stopTest to avoid mixed DML error
            Test.startTest();
            // Delete the account. 
            // Group is deleted through future method call in trigger.
            delete ac;
            Test.stopTest();
        }
    }
}

This is the account insert trigger that inserts a group.

trigger Account_After_Insert_Trg on Account (after insert) {
    Group gr = new Group(Name='Test',Type='Regular');
    insert gr;
}

This is the account delete trigger that calls a future method to delete a group.

trigger Account_Before_Delete_Trg on Account (before delete) {
    Util.deleteGroup('Test');
}

This is the future method that deletes a group.

public with sharing class Util {
    @future
    public static void deleteGroup(String grNameSet) {
        List<Group> grList = 
            [select Id, Name from Group where Name = :grNameSet];
        delete grList[0];
    }
}
Previous
Next