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
James Byron 26James Byron 26 

Problem creating or editing object in Test

I have an issue with creating tests for a class that calculates a number and stores it i my salesforce database.  I have a cstom object called "Application," which is related to a Contact object.  I created a custom field in the Application object that will store the numberical value of a students for finacial aid purposes.  I created an apex class to update the value of this number, and I am using that class through an after update trigger on the Application object.  The API name for the Application object is EnrollmentrxRx__Enrollment_Opportunity__c.  Here is the error that I'm getting:
System.DmlException: Update failed. First exception on row 0 with id a0Bo00000047ZJSEA2; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, ApplicationTrigger: execution of AfterUpdate
caused by: System.FinalException: Record is read-only
Class.CalculateFinAidIndex.updateIndex: line 67, column 1
Class.CalculateFinAidIndex.updateIndexFromApplication: line 26, column 1
Trigger.ApplicationTrigger: line 14, column 1: []

The stack trace says "Class.TestCalculateFinAidIndex.updateIndexFromApplicationTest: line 12, column 1," which points to the update apps line in the following code.  My Test class is below:
@isTest(SeeAllData=true)
public class TestCalculateFinAidIndex {
    static testMethod void updateIndexFromApplicationTest() {
        List<EnrollmentrxRx__Enrollment_Opportunity__c> apps = new List<EnrollmentrxRx__Enrollment_Opportunity__c>();
        apps.addall([SELECT Financial_Aid_Index__c,EnrollmentrxRx__Applicant__c,EnrollmentrxRx__Admissions_Status__c,Active_Application__c,Max_ACT_Composite__c,SAT_Superscore__c,EnrollmentrxRx__GPA__c,First_Generation__c FROM EnrollmentrxRx__Enrollment_Opportunity__c WHERE EnrollmentrxRx__Admissions_Status__c = 'Admit' AND Active_Application__c = true LIMIT 10]);
        System.assert(apps != null);
        System.assert(apps.size() > 0);
        for (EnrollmentrxRx__Enrollment_Opportunity__c app : apps) {
            app.Date_Last_Updated_Auto_FinAidIndex__c = Date.today();
        }
        update apps; // this will call the trigger
        for (Integer i = 0; i < apps.size(); i++) {
            System.assert(apps[i].Financial_Aid_Index__c != null);
            System.assert(apps[i].Financial_Aid_Index__c > 0);
        }
    }
}

Here's my code for the class that I need to test:
public class CalculateFinAidIndex {
    public static void updateIndexFromApplication(EnrollmentrxRx__Enrollment_Opportunity__c[] apps) {
        for (EnrollmentrxRx__Enrollment_Opportunity__c app : apps) {
            updateIndex(app);
        }
        update apps;
    }
    
    public static void updateIndex(EnrollmentrxRx__Enrollment_Opportunity__c app) {
            if (app.Active_Application__c &&
                ((app.EnrollmentrxRx__Admissions_Status__c == 'Applied') || (app.EnrollmentrxRx__Admissions_Status__c == 'Complete') || (app.EnrollmentrxRx__Admissions_Status__c == 'Admit'))) {
                    Integer finIndex = 0;
                    Double act = app.Max_ACT_Composite__c;
                    Double sat = app.SAT_Superscore__c; // or is it app.EnrollmentrxRx__Total_SAT_Score__c
                    Double gpa = 0; // 25
                    Integer gender = 0; // 10
                    Integer race = 0; // 10
                    Integer legacy = 0; // 10
                    Integer firstGen = 0; // 10
                    Double testValue = 0;
                    if (sat != null) sat = (sat / 2400) * 25; // assuming that the max is 2400
                    else sat = 0;
                    if (act != null) act = (act / 36) * 25; //assuming that the max is 36
                    else act = 0;
                    if (sat > act) testValue = sat;
                    else testValue = act;
                    Contact c = new Contact();//app.EnrollmentrxRx__Applicant__c;
                    if (c.EnrollmentrxRx__Gender__c == 'Male') gender = 10;
                    if (app.EnrollmentrxRx__GPA__c != null) {
                        gpa = (app.EnrollmentrxRx__GPA__c / 4) * 25;
                        if (gpa > 25) gpa = 25;
                    }
                    if (c.Race__c != 'White') race = 10;
                    if (app.First_Generation__c) firstGen = 10;
                    if (c.Legacy_Relationship__c != null) legacy = 10;
                    finIndex += (Integer)testValue + (Integer)gpa + gender + race + legacy + firstGen;
                    app.Financial_Aid_Index__c = finIndex;
                    System.debug('Logging from UpdateIndex: ' + finIndex);
            }
        }
}

Thank you for your help!
Best Answer chosen by James Byron 26
ArmouryArmoury
I am assuming that the line 67 in UpdateIndex() method in the original class is 
 
app.Financial_Aid_Index__c = finIndex;
The parameter which you are passing to the updateIndexFromApplication() method is probably from 'trigger.new' list which is a 'read only' list for all the after triggers. else its trigger.old which is always a read only list. Can you check the following link and update the code accordingly..

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_context_variables_considerations.htm
 

All Answers

ArmouryArmoury
I am assuming that the line 67 in UpdateIndex() method in the original class is 
 
app.Financial_Aid_Index__c = finIndex;
The parameter which you are passing to the updateIndexFromApplication() method is probably from 'trigger.new' list which is a 'read only' list for all the after triggers. else its trigger.old which is always a read only list. Can you check the following link and update the code accordingly..

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_context_variables_considerations.htm
 
This was selected as the best answer
James Byron 26James Byron 26
Thank you for the answer.  I changed the triger to before update and removed the DML calls.  In my test I queried the database after the trigger runs to check if each Application record has a valid value, which it does.  Now it appears to be working as expected.  Thanks!