+ Start a Discussion
JimmyATLJimmyATL 

Testing Undelete Triggers?

Hi All,

 

I'm new here, so hopefully I don't say anything too silly/dumb.

 

I am having a problem with testing an undelete trigger I have written.  I am getting an DMLException for de-referenfing a null object when trying to test my undelete trigger.  It may be that I am misunderstanding completely how testing of Apex undeletes should work... 

 

My basic requirements are that I have an order, and I have a field on that order called Needs Attn.  I need to set this field to True whenever the order is updated or when it is undeleted.  

 

My Trigger in JGOrderUpdate.trigger:

 

trigger JGOrderUpdate on JG__Order__c (before update, after undelete) {
	    for(JG__Order__c o : Trigger.new) {
		    JG__Order__c old = Trigger.oldMap.get(o.Id);
		    if(!old.Needs_Attn__c) o.Needs_Attn__c = true;
		}
	}

 

My Test Code JGTriggerTests.cls :

 

    // create a order
    JG__Order__c o = new JG__Order__c();
    o.Needs_Attn__c = false;
    insert o;

    // this kicks off the before update trigger for orders
    update o;

     // Confirms Actual update tests for orders
     JG__Order__c test_o = [Select Id, Needs_Attn__c From JG__Order__c Where Id =: o.Id];
     System.assert(test_o.Needs_Attn__c);

     // now we set the order back to needing attention false
     o.Needs_Attn__c = false;
	
     // now we delete the order so we can undelete it to make sure when we undelete it actually
     // changes to Needs_Attn__c == True
     delete o;
	
     // this kicks off the undelete trigger code
     // HAVING THE PROBLEM ON THE LINE BELOW
     undelete o;

     // did the trigger run properly
     System.assert(test_so.Needs_Attn__c);

 

 

Here's the error i get on the line above with "undelete o;"

 

 

Description Resource Path Location Type
 System.DmlException: Undelete failed. 
 first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, JGOrderUpdate: execution of AfterUndelete
 caused by: System.NullPointerException: Attempt to de-reference a null object
 Trigger.JGOrderUpdate: line 3, column 38: [] JGTriggerTests.cls
 Description Resource Path Location TypeSystem.DmlException: Undelete failed.  
 first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, JGOrderUpdate: execution of AfterUndelete
 caused by: System.NullPointerException: Attempt to de-reference a null object
 Trigger.JGOrderUpdate: line 3, column 38: [] JGTriggerTests.cls

Description Resource Path Location Type System.DmlException: Undelete failed.  first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, JGOrderUpdate: execution of AfterUndelete caused by: System.NullPointerException: Attempt to de-reference a null object Trigger.JGOrderUpdate: line 3, column 38: [] JGTriggerTests.cls Description Resource Path Location TypeSystem.DmlException: Undelete failed.   first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, JGOrderUpdate: execution of AfterUndelete caused by: System.NullPointerException: Attempt to de-reference a null object Trigger.JGOrderUpdate: line 3, column 38: [] JGTriggerTests.cls

 

 

 

Any help would be greatly appreciated!

 

Thanks
j

Best Answer chosen by Admin (Salesforce Developers) 
Shashikant SharmaShashikant Sharma

Sorry chnge this from

 

 

listJG.add(new JG__Order__c(id = o.id , o.Needs_Attn__c = true));

 

 

to

 

 

listJG.add(new JG__Order__c(id = o.id , Needs_Attn__c = true));

 

dont use o.Need_Attn__c 

 

All Answers

MJ09MJ09

For undelete triggers, only Trigger.new is usable. Trigger.old doesn't exist in the context of an undelete trigger.

 

 

JimmyATLJimmyATL

so should i separate my undelete trigger from my update trigger?

 

also, how do you test an undelete trigger if you can't do something like this:

 

insert object_x;

delete object_x;

undelete object_x;

 

Because that last line causes the dml exception...

MJ09MJ09

First, it's not the undelete line in your unit test that's causing the error. The undelete starts out just fine, but it's the fact that your undelete trigger is attempting to access Trigger.old that's causing the problem.

 

Second, if you want to change the value of a field, you have to make the change in Trigger.new, not in Trigger.old. Trigger.old reflects what used to be in the database before the change started. In a Before trigger, Trigger.new reflects what will be written to the database right after the Before trigger finishes. So make your change to Trigger.new, not Trigger.old.

 

Third, let's think about what you want your trigger to do. It looks like you're saying, "When a JG Order record is updated, the value of Needs Attn is false, set it to true." Is that what you really mean to be saying? Frankly, it seems a little odd -- why wouldn't you just say, "Set Needs Attn to true" and omit the IF statement? Can you simplify the code to remove any reference to Trigger.old?

 

Finally, unless you omit the IF statement as mentioned above, you may very well need to separate the Update and Undelete logic into two separate branches. Think about what you want the trigger to do, write it out fully in English (rather than in code), and only after that, translate it into code.

 

Hope that helps--

 

MJ.

MandyKoolMandyKool

Hi,

 

Your trigger is on "after undelete". So in after triggers you cannot change any value from the OldMap.

In your code you are trying to get the values from the OldMap. That is causing this exception.

 

In order to test your "after undelete" trigger you can seperate the "Before" and "After" triggers logic.

 

In your "after undelete" trigger logic, you can add following things.

 

if(isTrigger.undelete)

{

for(JG__Order__c o : Trigger.new) {
		    //JG__Order__c old = Trigger.oldMap.get(o.Id);
		    if(!o.Needs_Attn__c) o.Needs_Attn__c = true;
		} update o;

}

 

That should work for you!!

 

 

 

Shashikant SharmaShashikant Sharma

No in undelete trigger trigger.new is available but you can not update it. Your trigger should be like this

 

trigger JGOrderUpdate on JG__Order__c (before update, after undelete) {
	     List<JG__Order__c> listJG = new List<JG__Order__c>();
             for(JG__Order__c o : Trigger.new) {
                    if(trigger.isUpdate)
                    {
		    JG__Order__c old = Trigger.oldMap.get(o.Id);
		    if(!old.Needs_Attn__c) o.Needs_Attn__c = true;
                    }
                    else
                    {
                       if(!o.Needs_Attn__c)
                       listJG.add(id = o.id , o.Needs_Attn__c = true);
                    }

		}

              if(listJG.size() > 0)
                 update listJG;
	}

 

 

 

JimmyATLJimmyATL

Wow, you guys are amazing.  I was worried there were going to be crickets.

 

So I am trying out both possible solutions of splitting it up or keeping it together.  Honestly, I like the idea of keeping them together (not sure why though).

 

One quesiton for Mr. Sharma...

 

On this line:

 

listJG.add(id = o.id , Needs_Attn__c = true);

My IDE won't let me save the file because it says the variable id does not exist.  It puts a red x next to the line above.  But I think I know what you were trying to do something like this:

 

 

if(!o.Needs_Attn__c) {
    o.Needs_Attn__c = true;
    listJG.add(o);
}

 

Does this do the same thing or are you doing something entirely different?  My IDE allows me to save it this way... weird.

 

 

Thanks again!

j

 

Shashikant SharmaShashikant Sharma

Yes I wanted to do the same , actualy here on provding solution I can not compile it so some times these small typing mistakes do happen, happy that my solution worked for you. :)

Shashikant SharmaShashikant Sharma

What i suggested was

 

 

trigger JGOrderUpdate on JG__Order__c (before update, after undelete) {
	     List<JG__Order__c> listJG = new List<JG__Order__c>();
             for(JG__Order__c o : Trigger.new) {
                    if(trigger.isUpdate)
                    {
		    JG__Order__c old = Trigger.oldMap.get(o.Id);
		    if(!old.Needs_Attn__c) o.Needs_Attn__c = true;
                    }
                    else
                    {
                       if(!o.Needs_Attn__c)
                       listJG.add(new JG__Order__c(id = o.id , o.Needs_Attn__c = true));
                    }

		}
new JG__Order__c(id = o.id , o.Needs_Attn__c = true)

this is same what you updated it

just differnet way of adding it to list.

 

JimmyATLJimmyATL

Hey Guys,

 

Again thanks for the for the replies.  I decided to separate the triggers.  So Now I have these two:

 

The before update:

 

 

trigger JGOrderBeforeUpdate on JG__Order__c (before update) {
	for (JG__Order__c o : Trigger.new) {
	    JG__Order__c old = Trigger.oldMap.get(o.Id);
	    if (!old.Needs_Attn__c) o.Needs_Attn__c = true;
	}
}

 

And the after undelete:

 

 

 

trigger JGOrderAfterUndelete on JG__Order__c (after undelete) {
	List<JG__Order__c> listJGOs = new List<JG__Order__c>();
	for(JG__Order__c o : Trigger.new) {
		if(!o.Needs_Attn__c) {
		    o.Needs_Attn__c = true;
		    listJGOs.add(o);
		} 
	}
	update listJGOs;
}

 

 

Alright, and my original problem persits.  I cannot test the undelete because of the same exact DML Exception as listed the first time.

 

Here is my test code:

 

 

static testMethod void myUnitTest() {
        
        // create a sales order 
        JG__Order__c o = new JG__Order__c();
        o.Needs_Attn__c = false;
        insert o;
        
        update o;
        
        // Actual update tests for orders
        JG__Order__c test_o = [Select Id, Needs_Attn__c From JG__Order__c Where Id =: o.Id];
        System.assert(test_o.Needs_Attn__c);
        
        o.Needs_Attn__c = false;
        update o;
        
        delete o;
        undelete o; // I GET THE DML EXCEPTION ON THIS LINE.
        
        // Actual undelete tests for orders 
        JG__Order__c ud_test_o = [Select Id, Needs_Attn__c From JG__Order__c Where Id =: o.Id];
        System.assert(ud_test_o.Needs_Attn__c);
        
    }

 

I have removed any form of Trigger  old from the undelete as everyone has suggested, but I still have the same DML Error.  

 

Is there anything else you guys can see with my code causing the undelete o; to fail?  is this even the correct way to test an undelete.  There seems to be much less documenation on undeletes out there than updates and inserts for triggers.

 

Thanks Again,

j

 

JimmyATLJimmyATL

This is so odd...

 

If I delete the line above the delete o; line then the test case runs fine.  No DML Exception...

 

 

o.Needs_Attn__c = false;
update o;  // IF I DELETE THIS LINE, THE TEST CASE RUNS FINE (NO DML EXCEPTION)
        
delete o;
undelete o; 

 

 

Can anyone explain why this is happening?

 

To add to this problem... I now have only 33% test coverage on my undelete trigger.  It is saying these two lines lack test cases:

 

 

    o.Needs_Attn__c = true;
    listJGOs.add(o);

 I understand there is a learning curve and I appreciate any help.  But it seems like based on my previous test code (with the update o;) I would have had 100% test coverage, but now in order to even run the test case, I have to remove that and have less coverage.

 

 

Any ideas?

 

j

Shashikant SharmaShashikant Sharma

Try this if you don't want to separete

 

trigger JGOrderUpdate on JG__Order__c (before update, after undelete) {
	     List<JG__Order__c> listJG = new List<JG__Order__c>();
             for(JG__Order__c o : Trigger.new) {
                    if(trigger.isUpdate)
                    {
		    JG__Order__c old = Trigger.oldMap.get(o.Id);
		    if(!old.Needs_Attn__c) o.Needs_Attn__c = true;
                    }
                    else
                    {
                       if(!o.Needs_Attn__c)
                       listJG.add(new JG__Order__c(id = o.id , o.Needs_Attn__c = true));
                    }

		}

 If you want seperate then chnge your delete like this

 

trigger JGOrderAfterUndelete on JG__Order__c (after undelete) {
	List<JG__Order__c> listJGOs = new List<JG__Order__c>();
	for(JG__Order__c o : Trigger.new) {
		if(!o.Needs_Attn__c){
                       listJG.add(new JG__Order__c(id = o.id , o.Needs_Attn__c = true));
                 }
	}
	update listJGOs;
}

 

 

 

 

 

 

JimmyATLJimmyATL

My original problem is still the same.  I cannot test this code.  

 

Unfortunately, I cannot mark any of these as solutions either, becase none of these work inside IDE.  The IDE won't accept the code whether it is a syntax or logical error.  I posted the reasons why the IDE won't accept it earlier.

 

Here is how I got it to do what you are trying to do, but still this causes errors when I try to test the code....

 

 

trigger JGOrderAfterUndelete on JG__Order__c (after undelete) {
	List<JG__Order__c> listJGOs = new List<JG__Order__c>();
	for(JG__Order__c o : Trigger.new) {
		if(!o.Needs_Attn__c){
                       o.Needs_Attn__c = true;
                       listJGOs.add(o);
                 }
	}
	update listJGOs;
}

 

So, I am stuck.  How do you test an undelete without getting the DML Exception?

 

I have sent my test code, errors I got previously.  They are all still the same no matter what solution I use in this thread...

 

Thanks for any help.

 

j

 

 

Shashikant SharmaShashikant Sharma

Please use this for undelete trigger

 

trigger JGOrderAfterUndelete on JG__Order__c (after undelete) {
	List<JG__Order__c> listJGOs = new List<JG__Order__c>();
	for(JG__Order__c o : Trigger.new) {
		if(!o.Needs_Attn__c){
                       listJG.add(new JG__Order__c(id = o.id , o.Needs_Attn__c = true));
                 }
	}
	update listJGOs;
}

 This should work

 

JimmyATLJimmyATL

Unfortunately, this does not work

 

Here is the error I get:

 

 

Description Resource Path Location Type
Save error: Invalid field initializer: o.Needs_Attn__c JGOrderAfterUndelete.trigger /JGTest/src/triggers line 5 Force.com save problem

 

Description Resource Path Location TypeSave error: Invalid field initializer: o.Needs_Attn__c JGOrderAfterUndelete.trigger /JGTest/src/triggers line 5 Force.com save problem

 

The error appears as a red x next to this line in your code:

 

 

                       listJG.add(new JG__Order__c(id = o.id , o.Needs_Attn__c = true));

 

 

Shashikant SharmaShashikant Sharma

Sorry chnge this from

 

 

listJG.add(new JG__Order__c(id = o.id , o.Needs_Attn__c = true));

 

 

to

 

 

listJG.add(new JG__Order__c(id = o.id , Needs_Attn__c = true));

 

dont use o.Need_Attn__c 

 

This was selected as the best answer
JimmyATLJimmyATL

Very nice, finally it worked.  100% test coverage!!!

 

Wow that was fun.  On to the next trigger.

Shashikant SharmaShashikant Sharma

Please don't  look only for test coverage, cover all posible scenarios and test fr bulk data as well.