+ Start a Discussion
XactiumBenXactiumBen 

MIXED_DML_OPERATION in TestMethods

I am trying to create a testmethod for a trigger but I'm running into MIXED_DML_OPERATION error in the Salesforce Run Tests UI. What I do is create an EmailTemplate object in the test (just in case a certain org doesn't have any EmailTemplate records) then creating a Case which fires the trigger. In Eclipse I get 100% code coverage (plus no errors) but in the Salesforce UI I get the MIXED_DML_OPERATION error. Is there any way to fix this issue, like define inserting the EmailTemplate as a setup task that has nothing do to with how my trigger performs?
Best Answer chosen by Admin (Salesforce Developers) 
wesnoltewesnolte

Hey

 

We've just found a workaround for this issue. Although the workaround contains a salesforce bug, and we had to create a workaround for that too. We spoke to salesforce about it and logged a case but no info back yet. The workaround is liek this

 

 

@future private static void insertEmailTemplates(String emailTemplateName){ Folder myFolder = [select id from Folder where developerName = :emailTemplateFolder]; folderId = myFolder.id; List<EmailTemplate> listEmailTemplate = [select id from EmailTemplate where folderId = :folderId]; delete listEmailTemplate; EmailTemplate mailTemplate= new EmailTemplate(folderId = myFolder.id, body='yo', developerName=emailTemplateName, name=emailTemplateName,TemplateType='Text', subject='yo', isactive=true, encoding='UTF-8' ); insert mailTemplate; } public static void initEmailTemplates(String emailTemplateName){ if(user==null){ Integer countUser = [select count() from User]; if(countUser == 1){ user = new User(); user.Username = 'test2@us.xyz.com'; user.LastName = 'Last2TestName'; user.Email = 'test2@us.xyz.com'; user.alias = 'testA2'; user.TimeZoneSidKey = 'America/New_York'; user.LocaleSidKey = 'en_US'; user.EmailEncodingKey = 'ISO-8859-1'; user.ProfileId = [select id from Profile where Name='System Administrator'].Id; user.LanguageLocaleKey = 'en_US'; System.debug('getLimitRunAs: '+Limits.getLimitRunAs()); }else{ user = [select Username ,LastName,Email,alias,TimeZoneSidKey,LocaleSidKey,EmailEncodingKey,ProfileId,LanguageLocaleKey from User where id !=:UserInfo.getUserId() LIMIT 1]; } System.runAs(user){ Test.startTest(); insertEmailTemplates(emailTemplateName); Test.stopTest(); } } }

 So what's happening here is you're inserting emailtemplates in a different context by using @future. Except that there is a bug and this doesn't actually happen so you have to use a runAs() with the @future method for it to work. The Test.startTest() and Test.stopTest() force the asynchronous @future method to work 'synchronously'.

 

This works for me everywhere except in managed-released packages where I have to tweak it a bit.

 

Good luck

Wes

 

 

All Answers

wesnoltewesnolte

Hey

 

We've just found a workaround for this issue. Although the workaround contains a salesforce bug, and we had to create a workaround for that too. We spoke to salesforce about it and logged a case but no info back yet. The workaround is liek this

 

 

@future private static void insertEmailTemplates(String emailTemplateName){ Folder myFolder = [select id from Folder where developerName = :emailTemplateFolder]; folderId = myFolder.id; List<EmailTemplate> listEmailTemplate = [select id from EmailTemplate where folderId = :folderId]; delete listEmailTemplate; EmailTemplate mailTemplate= new EmailTemplate(folderId = myFolder.id, body='yo', developerName=emailTemplateName, name=emailTemplateName,TemplateType='Text', subject='yo', isactive=true, encoding='UTF-8' ); insert mailTemplate; } public static void initEmailTemplates(String emailTemplateName){ if(user==null){ Integer countUser = [select count() from User]; if(countUser == 1){ user = new User(); user.Username = 'test2@us.xyz.com'; user.LastName = 'Last2TestName'; user.Email = 'test2@us.xyz.com'; user.alias = 'testA2'; user.TimeZoneSidKey = 'America/New_York'; user.LocaleSidKey = 'en_US'; user.EmailEncodingKey = 'ISO-8859-1'; user.ProfileId = [select id from Profile where Name='System Administrator'].Id; user.LanguageLocaleKey = 'en_US'; System.debug('getLimitRunAs: '+Limits.getLimitRunAs()); }else{ user = [select Username ,LastName,Email,alias,TimeZoneSidKey,LocaleSidKey,EmailEncodingKey,ProfileId,LanguageLocaleKey from User where id !=:UserInfo.getUserId() LIMIT 1]; } System.runAs(user){ Test.startTest(); insertEmailTemplates(emailTemplateName); Test.stopTest(); } } }

 So what's happening here is you're inserting emailtemplates in a different context by using @future. Except that there is a bug and this doesn't actually happen so you have to use a runAs() with the @future method for it to work. The Test.startTest() and Test.stopTest() force the asynchronous @future method to work 'synchronously'.

 

This works for me everywhere except in managed-released packages where I have to tweak it a bit.

 

Good luck

Wes

 

 

This was selected as the best answer
XactiumBenXactiumBen

Thanks for that work around.  I made it a little bit easier by not getting a user out of the database but instead doing this:

 

System.runAs(new User(Id = System.UserInfo.getUserId())){ Test.startTest(); insertEmailTemplates(emailTemplateName); Test.stopTest(); }

 

This still seems to work :D

wesnoltewesnolte
Cool beans. The user bit I've put in there is just incase you don't have any user licenses left. Which is my case sometimes.