1

I wrote a trigger and need to use test the trigger by insert/update/delete large number

I wrote one method to test the insert/update/delete trigger and I ran into Apex CPU Limit.

My requirement is to test 1000 classes and each class has at least 600 student.

Here's a schema:

Contact-Student Master-Detail on Student

Class-Student Lookup on Student

The trigger is to get the number of different types of students under a class and set the value on the class field.

@isTest
public class Test_StudentTrigger {        
//static      
@isTest
public static void testInsert()
{   
    Test.startTest();
    List<Class__c> classes=new List<Class__c>();
    For(Integer i=0;i<1200;i++){
        Class__c c=new Class__c(name='c'+i);
        classes.add(c);
    }
    Insert classes;
    classes=[select id from class__c];
    System.debug('class num:'+classes.size());
    List<Student__c> students=new List<Student__c>();
    List<Contact> contacts=new List<Contact>();
    for(integer i=0;i<10000;i++){
        Contact c=new Contact(firstname='f'+i,lastname='l'+i);
        contacts.add(c);
    }
    Insert contacts;
    contacts=[select id from contact];
    System.debug('contacts num:'+contacts.size());

    For(Class__c c: classes)
    {
        Integer count=0;
        For(Integer i=0;i<250;i++)
        {               
            Student__c s=new Student__c(Contact__c=contacts[count++].Id,Class__c=c.Id,Status__c='Interested');
            students.add(s);
        }
        For(Integer i=0;i<150;i++)
        {
            Student__c s=new Student__c(Contact__c=contacts[count++].Id,Class__c=c.Id,Status__c='Verbally Committed');
            students.add(s);
        }
        For(Integer i=0;i<400;i++)
        {
            Student__c s=new Student__c(Contact__c=contacts[count++].Id,Class__c=c.Id,Status__c='Registered and Paid');
            students.add(s);
        }
    }
    insert Students;
    System.debug('line 43 num student: '+students.size());
    classes=[select id,Interested_Students__c,Verbally_Committed_Students__c,Registered_Students__c,Number_of_Students__c from class__c];
    System.debug('Line 46 num classes: '+classes.size());
    For(Class__c c: classes)
    {
        System.assertEquals(250, c.Interested_Students__c);
        System.assertEquals(150, c.Verbally_Committed_Students__c);
        System.assertEquals(400, c.Registered_Students__c);
        System.assertEquals(800, c.Number_of_Students__c);
    }

    System.debug('end inset test');

    //test update trigger
    //
    //
    students=[select id,Status__c,Class__c from Student__c];
    System.debug('line 61 num student: '+students.size());
    For(Student__c s : students)
    {
        if(s.Status__c=='Interested')
        {
            s.Status__c='Registered and Paid';
        }            
    }

    update students;
    system.debug(students);
    classes=[select id,Interested_Students__c,Verbally_Committed_Students__c,Registered_Students__c,Number_of_Students__c from class__c];
    For(Class__c c: classes)
    {
        System.assertEquals(0, c.Interested_Students__c);
        System.assertEquals(150, c.Verbally_Committed_Students__c);
        System.assertEquals(650, c.Registered_Students__c);
        System.assertEquals(800, c.Number_of_Students__c);
    }
    students=[select id,Status__c,Class__c from Student__c];
    For(Student__c s : students)
    {
        if(s.Status__c=='Verbally Committed')
        {
            s.Status__c='Interested';
        }            
    }
    update students;
    classes=[select id,Interested_Students__c,Verbally_Committed_Students__c,Registered_Students__c,Number_of_Students__c from class__c];
    For(Class__c c: classes)
    {
        System.assertEquals(150, c.Interested_Students__c);
        System.assertEquals(0, c.Verbally_Committed_Students__c);
        System.assertEquals(650, c.Registered_Students__c);
        System.assertEquals(800, c.Number_of_Students__c);
    }
    List<Student__c> studentsToDelete=new List<Student__c>();
    students=[select id,Status__c,Class__c from Student__c];
    For(Student__c s : students)
    {
        if(s.Status__c=='Interested')
        {
            studentsToDelete.add(s);
        }            
    }
    delete studentsToDelete;
    studentsToDelete.clear();
    classes=[select id,Interested_Students__c,Verbally_Committed_Students__c,Registered_Students__c,Number_of_Students__c from class__c];
    For(Class__c c: classes)
    {
        System.assertEquals(0, c.Interested_Students__c);
        System.assertEquals(0, c.Verbally_Committed_Students__c);
        System.assertEquals(650, c.Registered_Students__c);
        System.assertEquals(650, c.Number_of_Students__c);
    }
    Test.stopTest();
}

}

public class StudentTriggerHandler{

//integer Interested=0;integer Registered=0;integer Verbally_Committed=0;Integer totalStudent=0;
//key:ClassID plus Status, value:Number of student
public static Map<String,Integer> interestedNumMaps=new Map<String,Integer>();
public static Map<String,Integer> RegisteredNumMaps=new Map<String,Integer>();
public static Map<String,Integer> CommittedNumMaps=new Map<String,Integer>();
//store all classes involved
public static Set<String> classIDs=new Set<String>();
public static void handleBeforeInsert(List<Student__c> students)
{
        For(Student__c s : students)
        {
            String key=s.Class__c;
            classIDs.add(s.Class__c);
            Integer CurrentValue=1;
            if(s.Status__c=='Registered and Paid')
            {
                if(RegisteredNumMaps.containsKey(key)){
                    CurrentValue=RegisteredNumMaps.get(key);
                    RegisteredNumMaps.put(key,CurrentValue+1);
                }
                else{
                    RegisteredNumMaps.put(key,CurrentValue);
                }
            }
            else if(s.Status__c=='Verbally Committed')
            {
                if(CommittedNumMaps.containsKey(key)){
                    CurrentValue=CommittedNumMaps.get(key);
                    CommittedNumMaps.put(key,CurrentValue+1);
                }
                else{
                    CommittedNumMaps.put(key,CurrentValue);
                }
            }
            else if(s.Status__c=='Interested')
            {
                if(interestedNumMaps.containsKey(key)){
                    CurrentValue=interestedNumMaps.get(key);
                    interestedNumMaps.put(key,CurrentValue+1);
                }
                else{
                    interestedNumMaps.put(key,CurrentValue);
                }
            }   
        }
    System.debug(CommittedNumMaps);
        List<Class__c> classes=[select Id,Number_of_Students__c,Interested_Students__c,Registered_Students__c,Verbally_Committed_Students__c
                           From Class__c where id in : classIDs];
        For(Class__c c: classes)
        {
            if(RegisteredNumMaps.containsKey(c.Id))
            {
                if(c.Registered_Students__c!=null)
                {
                    c.Registered_Students__c+=RegisteredNumMaps.get(c.Id);
                }
                else
                {
                    c.Registered_Students__c=RegisteredNumMaps.get(c.Id);
                }
                if(c.Number_of_Students__c!=null){
                    c.Number_of_Students__c+=RegisteredNumMaps.get(c.Id);}
                else
                {
                    c.Number_of_Students__c=RegisteredNumMaps.get(c.Id);
                }
            }
            if(interestedNumMaps.containsKey(c.Id))
            {
                if(c.Interested_Students__c!=null){
                    c.Interested_Students__c+=interestedNumMaps.get(c.Id);}
                else
                {
                    c.Interested_Students__c=interestedNumMaps.get(c.Id);                        
                }
                if(c.Number_of_Students__c!=null){
                 c.Number_of_Students__c+=interestedNumMaps.get(c.Id);
                }
                else
                {c.Number_of_Students__c=interestedNumMaps.get(c.Id);

                }
            }
            if(CommittedNumMaps.containsKey(c.Id))
            {
                if(c.Verbally_Committed_Students__c!=null){
                    c.Verbally_Committed_Students__c+=CommittedNumMaps.get(c.Id);
                }
                else
                {
                    c.Verbally_Committed_Students__c=CommittedNumMaps.get(c.Id);
                }
                if(c.Number_of_Students__c!=null){
                    c.Number_of_Students__c+=CommittedNumMaps.get(c.Id);
                }
                else
                {
                    c.Number_of_Students__c=CommittedNumMaps.get(c.Id);
                }
            }
        }
        update classes;

}
public static void handleBeforeUpdate(List<Student__c> newStudents,List<Student__c> OldStudents)
{
    Map<Id,String> newMap=new Map<Id,String>();
    Map<Id,String> oldMap=new Map<Id,String>();
    For(Student__c s:oldStudents)
    {
        oldMap.put(s.Id, s.Status__c);
    }
    For(Student__c s : newStudents)
    {
        String oldStatus=oldMap.get(s.Id);
        if(oldStatus!=s.Status__c)
        {
            String key=s.Class__c;
            classIDs.add(s.Class__c);  
            Integer CurrentValue=1;
            if(s.Status__c=='Registered and Paid')
            {
                if(RegisteredNumMaps.containsKey(key)){
                    CurrentValue=RegisteredNumMaps.get(key);
                    RegisteredNumMaps.put(key,CurrentValue+1);
                }
                else{
                    RegisteredNumMaps.put(key,CurrentValue);
                }

                if(oldStatus=='Verbally Committed')
                {
                    if(CommittedNumMaps.containsKey(key)){
                    CurrentValue=CommittedNumMaps.get(key);
                    CommittedNumMaps.put(key,CurrentValue-1);
                    }
                    else{
                        RegisteredNumMaps.put(key,-1);
                    }
                }
                else
                {
                    if(interestedNumMaps.containsKey(key)){
                    CurrentValue=interestedNumMaps.get(key);
                    interestedNumMaps.put(key,CurrentValue-1);
                    }
                    else{
                        interestedNumMaps.put(key,-1);
                    }
                }
            }
            else if(s.Status__c=='Verbally Committed')
            {
                if(CommittedNumMaps.containsKey(key)){
                    CurrentValue=CommittedNumMaps.get(key);
                    CommittedNumMaps.put(key,CurrentValue+1);
                }
                else{
                    CommittedNumMaps.put(key,CurrentValue);
                }
                if(oldStatus=='Interested')
                {
                    if(interestedNumMaps.containsKey(key)){
                    CurrentValue=interestedNumMaps.get(key);
                    interestedNumMaps.put(key,CurrentValue-1);
                    }
                    else{
                        interestedNumMaps.put(key,-1);
                    }
                }
                else
                {
                    if(RegisteredNumMaps.containsKey(key)){
                    CurrentValue=RegisteredNumMaps.get(key);
                    RegisteredNumMaps.put(key,CurrentValue-1);
                    }
                    else{
                        RegisteredNumMaps.put(key,-1);
                    }
                }
            }
            else if(s.Status__c=='Interested')
            {
                if(interestedNumMaps.containsKey(key)){
                    CurrentValue=interestedNumMaps.get(key);
                    interestedNumMaps.put(key,CurrentValue+1);
                }
                else{
                    interestedNumMaps.put(key,CurrentValue);
                }
                if(oldStatus=='Verbally Committed')
                {
                    if(CommittedNumMaps.containsKey(key)){
                    CurrentValue=CommittedNumMaps.get(key);
                    CommittedNumMaps.put(key,CurrentValue-1);
                    }
                    else{
                        CommittedNumMaps.put(key,-1);
                    }
                }
                else
                {
                    if(RegisteredNumMaps.containsKey(key)){
                    CurrentValue=RegisteredNumMaps.get(key);
                    RegisteredNumMaps.put(key,CurrentValue-1);
                    }
                    else{
                        RegisteredNumMaps.put(key,-1);
                    }
                }
            }   
        }           





        }
        List<Class__c> classes=[select Id,Number_of_Students__c,Interested_Students__c,Registered_Students__c,Verbally_Committed_Students__c
                           From Class__c where id in : classIDs];
        For(Class__c c: classes)
        {
            if(RegisteredNumMaps.containsKey(c.Id))
            {
                if(c.Registered_Students__c!=null){
                    c.Registered_Students__c+=RegisteredNumMaps.get(c.Id);}
                else
                {
                    c.Registered_Students__c=RegisteredNumMaps.get(c.Id);
                }
                //c.Number_of_Students__c+=RegisteredNumMaps.get(c.Id);
            }
            if(interestedNumMaps.containsKey(c.Id))
            {
                if(c.Interested_Students__c!=null){
                    c.Interested_Students__c+=interestedNumMaps.get(c.Id);}
                else
                {
                    c.Interested_Students__c=interestedNumMaps.get(c.Id);
                }
                 //c.Number_of_Students__c+=interestedNumMaps.get(c.Id);
            }
            if(CommittedNumMaps.containsKey(c.Id))
            {
                if(c.Verbally_Committed_Students__c!=null){
                    c.Verbally_Committed_Students__c+=CommittedNumMaps.get(c.Id);}
                else
                {c.Verbally_Committed_Students__c=CommittedNumMaps.get(c.Id);
                }
                // c.Number_of_Students__c+=CommittedNumMaps.get(c.Id);
            }
        }
        update classes;
}
public static void handleBeforeDelete(List<Student__c> students)
{
        For(Student__c s : students)
        {
            String key=s.Class__c;
            classIDs.add(s.Class__c);
            Integer CurrentValue=1;
            if(s.Status__c=='Registered and Paid')
            {
                if(RegisteredNumMaps.containsKey(key)){
                    CurrentValue=RegisteredNumMaps.get(key);
                    RegisteredNumMaps.put(key,CurrentValue+1);
                }
                else{
                    RegisteredNumMaps.put(key,CurrentValue);
                }
            }
            else if(s.Status__c=='Verbally Committed')
            {
                if(CommittedNumMaps.containsKey(key)){
                    CurrentValue=CommittedNumMaps.get(key);
                    CommittedNumMaps.put(key,CurrentValue+1);
                }
                else{
                    CommittedNumMaps.put(key,CurrentValue);
                }
            }
            else if(s.Status__c=='Interested')
            {
                if(interestedNumMaps.containsKey(key)){
                    CurrentValue=interestedNumMaps.get(key);
                    interestedNumMaps.put(key,CurrentValue+1);
                }
                else{
                    interestedNumMaps.put(key,CurrentValue);
                }
            }   
        }
        List<Class__c> classes=[select Id,Number_of_Students__c,Interested_Students__c,Registered_Students__c,Verbally_Committed_Students__c
                           From Class__c where id in : classIDs];
        For(Class__c c: classes)
        {
            if(RegisteredNumMaps.containsKey(c.Id))
            {
                if(c.Registered_Students__c!=null){
                    c.Registered_Students__c-=RegisteredNumMaps.get(c.Id);
                }
                else
                {
                    c.Registered_Students__c=0-RegisteredNumMaps.get(c.Id);
                }
                if(c.Number_of_Students__c!=null){
                    c.Number_of_Students__c-=RegisteredNumMaps.get(c.Id);
                }
                else
                {
                    c.Number_of_Students__c=0-RegisteredNumMaps.get(c.Id);
                }

            }
            if(interestedNumMaps.containsKey(c.Id))
            {
                if(c.Interested_Students__c!=null){
                    c.Interested_Students__c-=interestedNumMaps.get(c.Id);
                }
                else
                {
                    c.Interested_Students__c=0-interestedNumMaps.get(c.Id);
                }
                if(c.Number_of_Students__c!=null)
                {
                    c.Number_of_Students__c-=interestedNumMaps.get(c.Id);
                }
                else
                {
                    c.Number_of_Students__c=0-interestedNumMaps.get(c.Id);
                }

            }
            if(CommittedNumMaps.containsKey(c.Id))
            {
                if(c.Verbally_Committed_Students__c!=null){
                    c.Verbally_Committed_Students__c-=CommittedNumMaps.get(c.Id);
                }
                else
                {
                    c.Verbally_Committed_Students__c=0-CommittedNumMaps.get(c.Id);
                }
                if(c.Number_of_Students__c!=null)
                {
                    c.Number_of_Students__c-=CommittedNumMaps.get(c.Id);
                }
                else
                {
                    c.Number_of_Students__c=0-CommittedNumMaps.get(c.Id);
                }

            }
        }
        update classes;

}

}

Modified Initiate Method:  Still ran into Apex CPU limit when running for loop
static List<Student__c> students=new List<Student__c>();
@testSetup
public static void setup() {
    List<Class__c> classes=Test.loadData(Class__c.sObjectType,'test_classes');        
    System.debug('class num:'+classes.size());
    List<Contact> contacts=Test.loadData(Contact.sObjectType,'test_contacts');
    System.debug('contacts num:'+contacts.size());       
    For(Class__c c: classes)
    {
        Integer count=0;
        For(Integer i=0;i<600;i++){               
            Student__c s=new Student__c(Contact__c=contacts[count++].Id,Class__c=c.Id,Status__c='Interested');
            students.add(s);
        }
    }

}
3
  • That is NOT a large number of records. you need to do some profiling of your code to see where the issue is. Either you have a stuck loop or you have some extremely inefficient code – Eric Feb 23 '15 at 19:49
  • Eric, How can I change the nested for loop – sfdcnoob Feb 23 '15 at 20:31
  • 2
    try taking the count down to 10 and see if you still have the issue. There is no need to do 600 records. If it works with 201 then you should be fine. Remember, in real life you cannot create records in this way so you will never have this issue. (If the issue is even because of this). If your code works for 10 records then it will calculate results as expected. Then you can try it will 10 cases and 201 students. That should test your logic fine. No need for 1000 and 600 – Eric Feb 23 '15 at 21:01
2

Rather than one large test method have you tried separating out each of your scenarios (i.e. each insert, update, delete) into separate test methods? You get a new set of limits for each method.

Further to this you should try to use Test.startTest() and Test.stopTest() closer to the actual methods being tested (i.e. the inserts, updates and deletes), as these will reset your limits before those operations run meaning that all of your setup code's CPU time will be excluded (unless it goes over on it's own).

You can also move all of your setup code out of your test methods using the new @testSetup annotation.

Here is an example using your code (PS. I have no idea if your asserts will still pass because I don't know how dependent they are on the previous steps, this could also be tidied up more but I'll leave that up to you).

@isTest
public class Test_StudentTrigger {   
    @testSetup
    public static void setup() {
        List<Class__c> classes=new List<Class__c>();
        For(Integer i=0;i<1200;i++){
            Class__c c=new Class__c(name='c'+i);
            classes.add(c);
        }
        Insert classes;
        classes=[select id from class__c];
        System.debug('class num:'+classes.size());
        List<Contact> contacts=new List<Contact>();
        for(integer i=0;i<10000;i++){
            Contact c=new Contact(firstname='f'+i,lastname='l'+i);
            contacts.add(c);
        }
        Insert contacts;
        contacts=[select id from contact];
        System.debug('contacts num:'+contacts.size());
    }

    //static      
    @isTest
    public static void testInsert()
    {           
        List<Contact> contacts=[select id from contact];
        List<Class__c> classes=[select id from class__c];
        List<Student__c> students=new List<Student__c>();

        For(Class__c c: classes)
        {
            Integer count=0;
            For(Integer i=0;i<250;i++)
            {               
                Student__c s=new Student__c(Contact__c=contacts[count++].Id,Class__c=c.Id,Status__c='Interested');
                students.add(s);
            }
            For(Integer i=0;i<150;i++)
            {
                Student__c s=new Student__c(Contact__c=contacts[count++].Id,Class__c=c.Id,Status__c='Verbally Committed');
                students.add(s);
            }
            For(Integer i=0;i<400;i++)
            {
                Student__c s=new Student__c(Contact__c=contacts[count++].Id,Class__c=c.Id,Status__c='Registered and Paid');
                students.add(s);
            }
        }

        Test.startTest();
        insert Students;
        Test.stopTest();

        System.debug('line 43 num student: '+students.size());
        classes=[select id,Interested_Students__c,Verbally_Committed_Students__c,Registered_Students__c,Number_of_Students__c from class__c];
        System.debug('Line 46 num classes: '+classes.size());
        For(Class__c c: classes)
        {
            System.assertEquals(250, c.Interested_Students__c);
            System.assertEquals(150, c.Verbally_Committed_Students__c);
            System.assertEquals(400, c.Registered_Students__c);
            System.assertEquals(800, c.Number_of_Students__c);
        }

        System.debug('end inset test');
    }

    @isTest
    public static void testUpdate())
    {
        List<Contact> contacts=[select id from contact];
        List<Class__c> classes=[select id from class__c];
        List<Student__c> students=new List<Student__c>();

        For(Class__c c: classes)
        {
            Integer count=0;
            For(Integer i=0;i<250;i++)
            {               
                Student__c s=new Student__c(Contact__c=contacts[count++].Id,Class__c=c.Id,Status__c='Interested');
                students.add(s);
            }
            For(Integer i=0;i<150;i++)
            {
                Student__c s=new Student__c(Contact__c=contacts[count++].Id,Class__c=c.Id,Status__c='Verbally Committed');
                students.add(s);
            }
            For(Integer i=0;i<400;i++)
            {
                Student__c s=new Student__c(Contact__c=contacts[count++].Id,Class__c=c.Id,Status__c='Registered and Paid');
                students.add(s);
            }
        }

        insert Students;

        //test update trigger
        //
        //
        students=[select id,Status__c,Class__c from Student__c];
        System.debug('line 61 num student: '+students.size());
        For(Student__c s : students)
        {
            if(s.Status__c=='Interested')
            {
                s.Status__c='Registered and Paid';
            }            
        }

        Test.startTest();
        update students;
        Test.stopTest();

        system.debug(students);
        classes=[select id,Interested_Students__c,Verbally_Committed_Students__c,Registered_Students__c,Number_of_Students__c from class__c];
        For(Class__c c: classes)
        {
            System.assertEquals(0, c.Interested_Students__c);
            System.assertEquals(150, c.Verbally_Committed_Students__c);
            System.assertEquals(650, c.Registered_Students__c);
            System.assertEquals(800, c.Number_of_Students__c);
        }
    }
}
2
  • Thank you Alex, I still ran into Apex CPU limit when the code executed to the for loops. – sfdcnoob Feb 23 '15 at 18:10
  • @JackWang I've just noticed you're creating 10,000 Contacts, is there a reason for so many? – Alex Tennant Feb 23 '15 at 20:32
1

If your test class is hitting gov limits it's a good indicator you may hit the same limits in production. This may not necessarily be the case since it looks like a lot of what you're doing is creating test data.

You should look into the test method loadData(). You can create all your test data in a csv file, upload it as a static reosource and load it into your test so you don't waste your CPU limit creating thousands of rows.

Spring '15 also has a new feature that lets you load test data once for an entire class

1
  • Rick, I posted the trigger handler. Thank you for giving me loadData() method and a nice article. I will try to use it to test the trigger. – sfdcnoob Feb 23 '15 at 17:48
0

Remove the For in the for loop, there are too many for loops in a for loop and if the same thing is done in the Actual class change it using maps instead of for loop in a for loop. Post your trigger code if possible.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.