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
SFDC 2017SFDC 2017 

Test class is failing with the below error Record is read only

Hi All,

We have ae afterinsertupdate trigger and we are calling handler class for that .Since we want the Id field of the lead so we used after insert and after update but now test class is failing with the below error:
System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, LeadTrigger: execution of AfterInsert

caused by: System.FinalException: Record is read-only.
Stack TraceClass:LeadAssignmentUtilTest.testLeadAssignment1: line 39, column 1
 private static testMethod void testLeadAssignment1() {
        createTestData();
        Test.startTest();
            insert testLead;
            Lead updatedLead = [SELECT Id, OwnerId FROM Lead WHERE ID =: testLead.Id LIMIT 1];
            system.assertEquals(updatedLead.OwnerId, standardUser.Id);
        Test.stopTest();
    }
I can,t change this to before insert because we need id of the Lead.some one please help me this is little urgent.

Thanks in advance,
 
GulshanRajGulshanRaj
Hi Archu,

Look like you are updating value using same reference of trigger.new.
Please note trigger.new values are available in read-only form only inside after trigger.

I would suggest to re-query in after trigger by collecting id's from trigger.newMap and then do neccessary changes. 
List<Lead> lstLeads = [SELECT Id,Name,customField__c FROM Lead WHERE Id IN: Trigger.newMap.keySet() ];

if(lstLeads!=null)
{
    for (Lead l : lstLeads) {
        l.customField__c = 'value';
    }
   update lstLeads;
}
I would suggest to use trigger helper classes instead of write code inside trigger.

Please let me know if you have any question.

Thanks
Gulshan Raj
 
SFDC 2017SFDC 2017
Thanks for your reply @Gulshan Raj.
I am using Trigger handler only in that i am calling afterinsertupdate method as follows:
public static void afterInsertUpdate(){
        Map<ID, Lead> oldMap = (Map<ID, Lead>)trigger.oldMap;
        List<Lead> newList =  (List<Lead>)Trigger.new;
        
        Map<String, Schema.Recordtypeinfo> rtMapLead = Schema.Sobjecttype.Lead.getRecordTypeInfosByName();
        Id salesLeadRecordTypeId = rtMapLead.get(Lead.CONST_SALES_LEAD_LABEL).getRecordTypeID();
        List<Lead> leadsToProcess = new List<Lead>();
for(Lead leadTemp : newList) {
            if(leadTemp.RecordTypeId == salesLeadRecordTypeId &&
                ((oldMap == null && leadTemp.Contact__c != null) || (oldMap != null && (leadTemp.Contact__c != oldMap.get(leadTemp.id).Contact__c ||
                    leadTemp.Lead_Routing_Type__c != oldMap.get(leadTemp.id).Lead_Routing_Type__c))) &&
                    leadTemp.Lead_Routing_Type__c == Lead.CONST_LEAD_ROUTING_TYPE
            ) {
                leadsToProcess.add(leadTemp);
            }
in this wat change i have to do i am adding into a list and if this list size >0 i am inserting record into another object and else i am calling other class

Thanks in advance
GulshanRajGulshanRaj
Here are few points:
1) You are again using List<Lead> newList =  (List<Lead>)Trigger.new; which is again refering to read-only values. Kindly note trigger.new is in read-only form in after trigger. During iteration you are adding same lead which came from trigger.new (Read-Only), which doesn't make difference.
2) Please refer Best Practices (https://developer.salesforce.com/page/Apex_Code_Best_Practices) and follow to "Bulkify your Helper Methods"
3) I would expect recursion in your trigger. Please follow this (https://help.salesforce.com/articleView?id=000133752&type=1) to avoid recursion.

I can provide you first iteration of your code from where you can proceed to do necessary changes mentioned above:
public static void afterInsertUpdate(){
        Map<ID, Lead> oldMap = (Map<ID, Lead>)trigger.oldMap;
        //re-query to refer new instance of leads instead of read-only leads
        List<Lead> newList =  [SELECT Id,Name,customField__c FROM Lead WHERE Id IN: Trigger.newMap.keySet() ];
        
        Map<String, Schema.Recordtypeinfo> rtMapLead = Schema.Sobjecttype.Lead.getRecordTypeInfosByName();
        Id salesLeadRecordTypeId = rtMapLead.get(Lead.CONST_SALES_LEAD_LABEL).getRecordTypeID();
        List<Lead> leadsToProcess = new List<Lead>();
for(Lead leadTemp : newList) {
            if(leadTemp.RecordTypeId == salesLeadRecordTypeId &&
                ((oldMap == null && leadTemp.Contact__c != null) || (oldMap != null && (leadTemp.Contact__c != oldMap.get(leadTemp.id).Contact__c ||
                    leadTemp.Lead_Routing_Type__c != oldMap.get(leadTemp.id).Lead_Routing_Type__c))) &&
                    leadTemp.Lead_Routing_Type__c == Lead.CONST_LEAD_ROUTING_TYPE
            ) {
                leadsToProcess.add(leadTemp);
            }
            //whatever your code
            }

            //Update leads
            update leadsToProcess;



All the best!!

Thanks
Gulshan Raj
 
GulshanRajGulshanRaj
Hi Archu,

Is this fix the issue?

Please let me know if you have any question.

Thanks
Gulshan Raj