As of the Spring 16 release, there is now another way to avoid an error that would often occur when running Apex unit tests. The MIXED_DML_OPERATION error would occur because you can’t perform DML on a setup sObject (such as User, for example) and a non-setup object (such as Contact) in the same transaction.
So, if you had some code such as the following:
@isTest
public class UserAndContactTest {
public testmethod static void testUserAndContact() {
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;
Contact currentContact = new Contact(
firstName = String.valueOf(System.currentTimeMillis()),
lastName = 'Contact');
insert(currentContact);
}
}
|
The unit test code above would fail with the MIXED_DML_OPERATION error.
Previously, the only way to get around it was to enclose all the operations within a System.runAs block, but now you have another alternative in which you can use @future to bypass the error. For example, the following class could contain the code used to insert the user:
public class InsertFutureUser {
@future
public static void insertUser() {
Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];
UserRole r = [SELECT Id FROM UserRole WHERE Name='COO'];
User futureUser = new User(firstname = 'Future', lastname = 'User',
alias = 'future', defaultgroupnotificationfrequency = 'N',
digestfrequency = 'N', email = 'test@test.org',
emailencodingkey = 'UTF-8', languagelocalekey='en_US',
localesidkey='en_US', profileid = p.Id,
timezonesidkey = 'America/Los_Angeles',
username = 'futureuser@test.org',
userpermissionsmarketinguser = false,
userpermissionsofflineuser = false, userroleid = r.Id);
insert(futureUser);
}
}
|
And then, you could just change the original code to be the following:
@isTest
public class UserAndContactTest {
public testmethod static void testUserAndContact() {
InsertFutureUser.insertUser();
Contact currentContact = new Contact(
firstName = String.valueOf(System.currentTimeMillis()),
lastName = 'Contact');
insert(currentContact);
}
}
|
And then there will be no more error. I think it is a better way of handling the issue than using runAs and should be considered when there is a need to run DML operations for setup and non-setup objects in the same unit test transaction.
Like this:
Like Loading...
I don’t think your new code for inserting user is ever executed in your test, since @future methods only run in tests when you run Test.startTest() or Test.stopTest().
Can you add an assertion to your test to make sure the user was actually inserted?