function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Caleb_SidelCaleb_Sidel 

Bug? with Custom Settings when attempting to get 100% code coverage (affects Packaging)

I recently tried to package an app with custom settings. Unfortunately I couldn't write test methods because I wanted to create custom settings instances to test the various areas of my code. The error message I got is well known to me: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa).

 

Since inserting a Custom Settings object is a setup operation I can not create a custom settings and then proceed to create my test data. I tried creating my custom settings inside an @future method and this didn't work either (I did wrap my @future call with Test.startTest() and Test.stopTest() to ensure my @future method completed (i.e. to force synchornous execution). But I still got the same error message.

 

Ultimately what I did was write my code in such a way that the Class which relied on the custom settings had a local variable for the custom settings which I could set. In normal operation my code will query the custom settings using .getInstance() however my testMethods will all override this normal behavior and "force" set the custom settings.

 

My class which uses custom settings is below as is my class which tests my code. I hope you find this useful, and if you spot anything wrong with this please let me know. Thanks,

Caleb

 

------------ Class --------------

 public without sharing class CrossObjectUtil {
 private Map<Id,gii__AccountAdd__c> childrenToUpdateParent;
 private List<Account> parentsToUpdate;
 private Boolean isTest = false;

//This is the key!
 private CrossObjectSettings__c crossObjSettingsInstance = null;
 
 public CrossObjectUtil() {
  childrenToUpdateParent = new Map<Id,gii__AccountAdd__c>();
  parentsToUpdate = new List<Account>();
  setCrossObjectSettings(null);
 }

//Used by the test methods!

 public void setCrossObjectSettings(CrossObjectSettings__c cos) {
  if(cos == null) {
   CrossObjectSettings__c crossObjSettingsInstance = null;
   Map<String,CrossObjectSettings__c> crossObjSettingsInstanceMap = CrossObjectSettings__c.getAll();
   
   for(CrossObjectSettings__c cosInstance : crossObjSettingsInstanceMap.values()) {
    if(cosInstance.Child_Object_Name__c == 'gii__AccountAdd__c'&&
       cosInstance.Parent_Object_Name__c == 'Account') {
        crossObjSettingsInstance = cosInstance;
       }     
   }
  } else {
   crossObjSettingsInstance = cos;
  }
 }
 
 public void addChildToUpdateParent(Id parentId, gii__AccountAdd__c child) {
  childrenToUpdateParent.put(parentId,child);
 }

 public void updateParents() {
  if (childrenToUpdateParent.size() > 0) {
   updateParents(childrenToUpdateParent,false);
  }
 
  update parentsToUpdate;
 }
 
 private void updateParents(Map<Id,gii__AccountAdd__c> children,Boolean isDelete) {
  System.debug('updateParents: isDelete = ' + isDelete);
  for(Id parentId : children.keySet()) {
    SObject parent = new Account(Id=parentId);
    SObject child = children.get(parentId);
        
    if(crossObjSettingsInstance == null) {
     setCrossObjectSettings(null);
     if(crossObjSettingsInstance == null) {
      return;
     }
    }
    
    List<String> fieldMappings = crossObjSettingsInstance.Child_to_Parent_Field_Mapping__c.split(';',0);
    
    if(fieldMappings.size() == 0) return;
    
    for(String mapping : fieldMappings) {
     List<String> fields = mapping.split('=',0);
     if(fields.size() == 2) {
      if(!isDelete) {
       parent.put(fields[1],child.get(fields[0]));
      } else {
       parent.put(fields[1],null);
      }
     }
    }
    parentsToUpdate.add((Account)parent);
   } 
 }
}

 

-----------Test Method -------------

    static testMethod void test_CrossObjectUtil_Self() {
     Account a = insertAccount();
     gii__Warehouse__c w = insertWharehouse();
     gii__AccountAdd__c acctAdd = getAccountAdd(a,w);
    
      CrossObjectUtil coUtil = new CrossObjectUtil();

//Set the custom settings - the object is never saved!
      coUtil.setCrossObjectSettings(createCustomSettings());
        coUtil.addChildToUpdateParent(a.Id,acctAdd);
        coUtil.updateParents();
        Account assertAccount = [SELECT Name, Description FROM Account WHERE Id =:a.Id LIMIT 1];
        System.assertEquals(w.Id,assertAccount.Name);
        System.assertEquals(acctAdd.Name,assertAccount.Description);
    }
      
 //Notice that I do NOT insert the object! it's all in memory!
    static CrossObjectSettings__c createCustomSettings() {
     CrossObjectSettings__c crossObjSettingsInstance = new CrossObjectSettings__c(Name = 'Test Account Add to Account');
     crossObjSettingsInstance.Child_Object_Name__c = 'gii__AccountAdd__c';
     crossObjSettingsInstance.Parent_Object_Name__c = 'Account';
     crossObjSettingsInstance.Child_to_Parent_Field_Mapping__c = 'Name=Description;gii__DefaultWarehouse__c=Name';
     return crossObjSettingsInstance;
    }

Best Answer chosen by Admin (Salesforce Developers) 
SankalpSankalp

Caleb_Sidel wrote:

I recently tried to package an app with custom settings. Unfortunately I couldn't write test methods because I wanted to create custom settings instances to test the various areas of my code. The error message I got is well known to me: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa).

 

Since inserting a Custom Settings object is a setup operation I can not create a custom settings and then proceed to create my test data. I tried creating my custom settings inside an @future method and this didn't work either (I did wrap my @future call with Test.startTest() and Test.stopTest() to ensure my @future method completed (i.e. to force synchornous execution). But I still got the same error message.

 

Ultimately what I did was write my code in such a way that the Class which relied on the custom settings had a local variable for the custom settings which I could set. In normal operation my code will query the custom settings using .getInstance() however my testMethods will all override this normal behavior and "force" set the custom settings.

 

My class which uses custom settings is below as is my class which tests my code. I hope you find this useful, and if you spot anything wrong with this please let me know. Thanks,

Caleb

 

------------ Class --------------

 public without sharing class CrossObjectUtil {
 private Map<Id,gii__AccountAdd__c> childrenToUpdateParent;
 private List<Account> parentsToUpdate;
 private Boolean isTest = false;

//This is the key!
 private CrossObjectSettings__c crossObjSettingsInstance = null;
 
 public CrossObjectUtil() {
  childrenToUpdateParent = new Map<Id,gii__AccountAdd__c>();
  parentsToUpdate = new List<Account>();
  setCrossObjectSettings(null);
 }

//Used by the test methods!

 public void setCrossObjectSettings(CrossObjectSettings__c cos) {
  if(cos == null) {
   CrossObjectSettings__c crossObjSettingsInstance = null;
   Map<String,CrossObjectSettings__c> crossObjSettingsInstanceMap = CrossObjectSettings__c.getAll();
   
   for(CrossObjectSettings__c cosInstance : crossObjSettingsInstanceMap.values()) {
    if(cosInstance.Child_Object_Name__c == 'gii__AccountAdd__c'&&
       cosInstance.Parent_Object_Name__c == 'Account') {
        crossObjSettingsInstance = cosInstance;
       }     
   }
  } else {
   crossObjSettingsInstance = cos;
  }
 }
 
 public void addChildToUpdateParent(Id parentId, gii__AccountAdd__c child) {
  childrenToUpdateParent.put(parentId,child);
 }

 public void updateParents() {
  if (childrenToUpdateParent.size() > 0) {
   updateParents(childrenToUpdateParent,false);
  }
 
  update parentsToUpdate;
 }
 
 private void updateParents(Map<Id,gii__AccountAdd__c> children,Boolean isDelete) {
  System.debug('updateParents: isDelete = ' + isDelete);
  for(Id parentId : children.keySet()) {
    SObject parent = new Account(Id=parentId);
    SObject child = children.get(parentId);
        
    if(crossObjSettingsInstance == null) {
     setCrossObjectSettings(null);
     if(crossObjSettingsInstance == null) {
      return;
     }
    }
    
    List<String> fieldMappings = crossObjSettingsInstance.Child_to_Parent_Field_Mapping__c.split(';',0);
    
    if(fieldMappings.size() == 0) return;
    
    for(String mapping : fieldMappings) {
     List<String> fields = mapping.split('=',0);
     if(fields.size() == 2) {
      if(!isDelete) {
       parent.put(fields[1],child.get(fields[0]));
      } else {
       parent.put(fields[1],null);
      }
     }
    }
    parentsToUpdate.add((Account)parent);
   } 
 }
}

 

-----------Test Method -------------

    static testMethod void test_CrossObjectUtil_Self() {
     Account a = insertAccount();
     gii__Warehouse__c w = insertWharehouse();
     gii__AccountAdd__c acctAdd = getAccountAdd(a,w);
    
      CrossObjectUtil coUtil = new CrossObjectUtil();

//Set the custom settings - the object is never saved!
      coUtil.setCrossObjectSettings(createCustomSettings());
        coUtil.addChildToUpdateParent(a.Id,acctAdd);
        coUtil.updateParents();
        Account assertAccount = [SELECT Name, Description FROM Account WHERE Id =:a.Id LIMIT 1];
        System.assertEquals(w.Id,assertAccount.Name);
        System.assertEquals(acctAdd.Name,assertAccount.Description);
    }
      
 //Notice that I do NOT insert the object! it's all in memory!
    static CrossObjectSettings__c createCustomSettings() {
     CrossObjectSettings__c crossObjSettingsInstance = new CrossObjectSettings__c(Name = 'Test Account Add to Account');
     crossObjSettingsInstance.Child_Object_Name__c = 'gii__AccountAdd__c';
     crossObjSettingsInstance.Parent_Object_Name__c = 'Account';
     crossObjSettingsInstance.Child_to_Parent_Field_Mapping__c = 'Name=Description;gii__DefaultWarehouse__c=Name';
     return crossObjSettingsInstance;
    }


 

hi,

 

try using

 

//Insert custom setting 

 

System.runas(adminuser)

                           {

                                //write your code here after inserting the Custom Setting

                            }

 

let me know, I can post a sample as well.

 

Thanks

Sankalp

All Answers

SankalpSankalp

Caleb_Sidel wrote:

I recently tried to package an app with custom settings. Unfortunately I couldn't write test methods because I wanted to create custom settings instances to test the various areas of my code. The error message I got is well known to me: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa).

 

Since inserting a Custom Settings object is a setup operation I can not create a custom settings and then proceed to create my test data. I tried creating my custom settings inside an @future method and this didn't work either (I did wrap my @future call with Test.startTest() and Test.stopTest() to ensure my @future method completed (i.e. to force synchornous execution). But I still got the same error message.

 

Ultimately what I did was write my code in such a way that the Class which relied on the custom settings had a local variable for the custom settings which I could set. In normal operation my code will query the custom settings using .getInstance() however my testMethods will all override this normal behavior and "force" set the custom settings.

 

My class which uses custom settings is below as is my class which tests my code. I hope you find this useful, and if you spot anything wrong with this please let me know. Thanks,

Caleb

 

------------ Class --------------

 public without sharing class CrossObjectUtil {
 private Map<Id,gii__AccountAdd__c> childrenToUpdateParent;
 private List<Account> parentsToUpdate;
 private Boolean isTest = false;

//This is the key!
 private CrossObjectSettings__c crossObjSettingsInstance = null;
 
 public CrossObjectUtil() {
  childrenToUpdateParent = new Map<Id,gii__AccountAdd__c>();
  parentsToUpdate = new List<Account>();
  setCrossObjectSettings(null);
 }

//Used by the test methods!

 public void setCrossObjectSettings(CrossObjectSettings__c cos) {
  if(cos == null) {
   CrossObjectSettings__c crossObjSettingsInstance = null;
   Map<String,CrossObjectSettings__c> crossObjSettingsInstanceMap = CrossObjectSettings__c.getAll();
   
   for(CrossObjectSettings__c cosInstance : crossObjSettingsInstanceMap.values()) {
    if(cosInstance.Child_Object_Name__c == 'gii__AccountAdd__c'&&
       cosInstance.Parent_Object_Name__c == 'Account') {
        crossObjSettingsInstance = cosInstance;
       }     
   }
  } else {
   crossObjSettingsInstance = cos;
  }
 }
 
 public void addChildToUpdateParent(Id parentId, gii__AccountAdd__c child) {
  childrenToUpdateParent.put(parentId,child);
 }

 public void updateParents() {
  if (childrenToUpdateParent.size() > 0) {
   updateParents(childrenToUpdateParent,false);
  }
 
  update parentsToUpdate;
 }
 
 private void updateParents(Map<Id,gii__AccountAdd__c> children,Boolean isDelete) {
  System.debug('updateParents: isDelete = ' + isDelete);
  for(Id parentId : children.keySet()) {
    SObject parent = new Account(Id=parentId);
    SObject child = children.get(parentId);
        
    if(crossObjSettingsInstance == null) {
     setCrossObjectSettings(null);
     if(crossObjSettingsInstance == null) {
      return;
     }
    }
    
    List<String> fieldMappings = crossObjSettingsInstance.Child_to_Parent_Field_Mapping__c.split(';',0);
    
    if(fieldMappings.size() == 0) return;
    
    for(String mapping : fieldMappings) {
     List<String> fields = mapping.split('=',0);
     if(fields.size() == 2) {
      if(!isDelete) {
       parent.put(fields[1],child.get(fields[0]));
      } else {
       parent.put(fields[1],null);
      }
     }
    }
    parentsToUpdate.add((Account)parent);
   } 
 }
}

 

-----------Test Method -------------

    static testMethod void test_CrossObjectUtil_Self() {
     Account a = insertAccount();
     gii__Warehouse__c w = insertWharehouse();
     gii__AccountAdd__c acctAdd = getAccountAdd(a,w);
    
      CrossObjectUtil coUtil = new CrossObjectUtil();

//Set the custom settings - the object is never saved!
      coUtil.setCrossObjectSettings(createCustomSettings());
        coUtil.addChildToUpdateParent(a.Id,acctAdd);
        coUtil.updateParents();
        Account assertAccount = [SELECT Name, Description FROM Account WHERE Id =:a.Id LIMIT 1];
        System.assertEquals(w.Id,assertAccount.Name);
        System.assertEquals(acctAdd.Name,assertAccount.Description);
    }
      
 //Notice that I do NOT insert the object! it's all in memory!
    static CrossObjectSettings__c createCustomSettings() {
     CrossObjectSettings__c crossObjSettingsInstance = new CrossObjectSettings__c(Name = 'Test Account Add to Account');
     crossObjSettingsInstance.Child_Object_Name__c = 'gii__AccountAdd__c';
     crossObjSettingsInstance.Parent_Object_Name__c = 'Account';
     crossObjSettingsInstance.Child_to_Parent_Field_Mapping__c = 'Name=Description;gii__DefaultWarehouse__c=Name';
     return crossObjSettingsInstance;
    }


 

hi,

 

try using

 

//Insert custom setting 

 

System.runas(adminuser)

                           {

                                //write your code here after inserting the Custom Setting

                            }

 

let me know, I can post a sample as well.

 

Thanks

Sankalp

This was selected as the best answer
DianeMDianeM

Sankalp, thank you for posting your suggestion.  I must admit I wasn't sure how running the test as the administrator would alleviate the problem but then I realized that by doing that, I am essentially running the test in a separate context than the one in which I set the custom setting.  This worked great!!!

 

Thanks,

 

Diane 

Rajesh ShahRajesh Shah

Thanks. Thats great.

rohan.chopra1.3941833609301624E12rohan.chopra1.3941833609301624E12
Test Class 

Important-----------------> @isTest(SeeAllData=true)

Code-

@isTest(SeeAllData=true)
public class TestClasses
{

    
    static testMethod void testPayment() {

       ...................
       ...................
    }
}