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
econ242econ242 

HELP on writing a test class...PLEASE!!!

Hello,

So I'm a newbie to writing triggers and need help on wrting a test class so I can deploy to production...here is my situation:

I have a trigger that updates a hidden field on an Account record that was needed in order to track changes on a seperate field that is a formula field. When the field value changes, the trigger runs and captures the previous value in the Account History. Here is my trigger code in my sandbox:

trigger BusinessRatingChange on Account (before update)
{
    for(Account a:Trigger.new)
    {
        if(system.trigger.OldMap.get(a.Id).Business_Rating__c != system.trigger.NewMap.get(a.Id).Business_Rating__c)
        {
            a.Business_Rating_History__c = a.Business_Rating__c;
            }
            }
            }

This trigger works perfect in my sandbox, but with my limited knowledge on triggers and writing test classes...is there anyone out there that can assist me, or guide me to where I can understand better?? Bad news is i need to get this deployed QUICK!!!  :(

Thanks so much!!!

Eric
James LoghryJames Loghry
Eric, this is probably one of the most frequently asked questions on here.  Try doing a quick search and you'll find lots of tips and tricks around creating tests for triggers.  In particular, check out this thread that I answered on the same topic yesterday: https://developer.salesforce.com/forums/ForumsMain?id=906F0000000AdhRIAS
Clay AshworthClay Ashworth
Eric,

I know what you are going through and understanding test methods for the first time when you are under pressure to deploy a trigger can be frustrating. As James mentioned there are lots of resources out there for writing effective test methods and it will serve you to read through the documentation so that you can get a detailed understanding of them. That said there are really only three things you need to know about writing a baisc test method for a trigger:

1. Make sure your class has the @Is Test annotation at the top.
2. Add records so that you have something to test.
3. Insert, Update, Delete records or fields that will cause the trigger to fire.

If you have if/else statements you will need to create conditions in your record manipulations that will cause each piece of code in the trigger to be tested.

Watch out for null pointer exceptions. use lots of If(trigger.new[0].Id != null) { } statements to mitigate these (be liberal with them).

Since you are in a hurry I drafted up this test class for you that shoud get you 100% code coverage on your trigger.

//Annotate class as test
@IsTest

//Name the class
private class AccountBeforeUpdateTest {
	
	//name the test method
	static testMethod void businesratingtest() {

	//Create and insert records for test
	Account a = new Account();
		a.Name=‘Test’;
		a.Business_Rating__c = ‘some value’;
	
	insert a;


	//Exercise the trigger by changing the field value of Business_Rating__c since that’s what makes it fire
	a.Business_Rating__c = ‘some other value’;

	update a;

	}

}



econ242econ242
Clay...you are AWESOME!!! This is exactly what i needed... sans the test class you wrote, you're explanation above was dead on what I was looking for...just a quick, brief explanation of the basics...this was SOOOO helpful!!!

I haven't tested the class yet...I understand almost all of what you explained...don't quite get this:  "Watch out for null pointer exceptions. use lots of If(trigger.new[0].Id != null) { } statements to mitigate these (be liberal with them)." - maybe you could elaborate slightly?

Regardless, you are a life saver...I can't thank you enough!!! This just unloaded a huge weight off my shoulders and will use this template to develop my other test classes for the other triggers that i have that are similar!!!

I'll let you know how it goes...thanks again Clay!!!  :)

Best,

Eric
Clay AshworthClay Ashworth
Eric,

Null Pointer Exceptions happen when you try to take action on a record or field value derived from a query where the query may not return a result. This could be a field that is null or just a lack of a record that meets the query criteria. Before you try to take an action such as use a field in another query or using a field value in an expression it is wise to check and make sure that it is not null first. 

In the Test Class example above on line 21 where I call for an update to vairable "a", I am assuming that "a" is not null. I know it's not because I just inserted it in the lines above but when a trigger is running and you have referenced another object, like the AccountID on a Contact for instance, there is no assurance that the Contact has a value in the AccountId field hense a null pointer check would be in order before you attempt to use that field value to do something else.

Anyway, glad I could help. If you find that this works out for you please mark this as Solved :-).

Regards,

Clay


econ242econ242
Hi Clay,

So I kinda goofed on my explanation...the Business_Rating__c field is a formula field derived from calculations on two other fields, so to test, I need to also insert a record that drives the change to this field so the triger can fire...I attemped this on my own but I keep getting an error...see my new code below:

//Annotate class as test
    @IsTest
    
    //Name the class
    private class AccountBeforeUpdateTest {
        
        //name the test method
        static testMethod void businesratingtest() {
    
        //Create and insert records for test
        Account a = new Account();
            a.Name='Test';
            a.RS_of_New_WC_Clients_Per_Month__c = '10';
        
        insert a;
    
        //Create and Insert new referral record for test
        New Referral's Received b = new New Referral's Received();
        b.Name='August 1, 2014';
        b.of_Referrals_Received__c='6';
        b.Account_Name__c='Test';
        b.Case_Type__c='Workers' Comp';
        b.Date__c='8/1/2014';
          
        insert b;
        
      //Exercise the trigger by changing the field value of Business_Rating__c since that’s what makes it fire
           
        update a;
    
        }
    
    }

Basically, this field above on the Account insert - a.RS_of_New_WC_Clients_Per_Month__c = '10' - drives the Rating after being calculated with another field, but when I try to save, I keep getting this error:

Error: Compile Error: line breaks not allowed in string literals at line 22 column -1

Any thoughts???
Clay AshworthClay Ashworth
looks like you have an extra single quote @ line 22
b.Case_Type__c='Workers' Comp';

You might also want to change value in the the b.Date__c field to
Date.newInstance(2013,10,10);

Assunming the of_Referrals_Received__c is a double or integer, you do not need single quotes to set the value

Changed the New_Referrals_Received__c to reference the custom object.

Made an assumption here that the New Refeerals Received is related to Account and has a field called Account__c on it. Added a line to associate the new record with the Account.
//Annotate class as test
    @IsTest
    
    //Name the class
    private class AccountBeforeUpdateTest {
        
        //name the test method
        static testMethod void businesratingtest() {
    
        //Create and insert records for test
        Account a = new Account();
            a.Name='Test';
            a.RS_of_New_WC_Clients_Per_Month__c = '10';
        
        insert a;
    
        //Create and Insert new referral record for test
        New_Referrals_Received__c b = new New_Referrals_Received__c>();
        b.Name='August 1, 2014';
        b.of_Referrals_Received__c=6;
        b.Account_Name__c=a.Name;
        b.Case_Type__c='Workers Comp';
        b.Date__c=Date.newInstance(2014,08,01);
//relate b to the Account
        b.Account__c = a.Id;
          
        insert b;

//  If the insertion of b will cause the field to update on the account record you should not need to update the account
        
//       update a;
    
        }
    
    }

econ242econ242
Clay...you are awesome...I think we almost have it!!!

I had to modify a couple things due to some validation rules...the only issue I am getting now is this error:

System.StringException: Invalid id: Test Econ Change
Class.AccountBeforeUpdateTest.businesratingtest: line 25, column 1

I figure that this is due to the field (Account_Name__c) on the object - New_Referrals_Received__c - is a lookup field, but it defaults to the running Account because that is how they are entered...here is my current code:

//Annotate class as test
    @IsTest
   
    //Name the class
    private class AccountBeforeUpdateTest {
       
        //name the test method
        static testMethod void businesratingtest() {
   
        //Create and insert records for test
        Account a = new Account();
            a.Name='Test Econ Change';
            a.BillingStreet='10 Main St';
            a.BillingCity='Tyngsboro';            
            a.BillingPostalCode='01879';
            a.BillingState='MA';          
            a.RS_of_New_WC_Clients_Per_Month__c = 10;
                   
        insert a;
   
        //Create and Insert new referral record for test
        Referral_s_Received__c b = new Referral_s_Received__c ();
        b.Name='August 1, 2014';
        b.of_Referrals_Received__c=6;
        b.Account_Name__c='Test Econ Change';
        b.Case_Type__c='Workers Comp';
        b.Date__c=Date.newInstance(2014,08,01);
//relate b to the Account
        b.Account_Name__c = a.Id;
         
        insert b;

//  If the insertion of b will cause the field to update on the account record you should not need to update the account
       
//       update a;
   
        }
   
    }

I tried changing that field to reflect the name, but that is not right either...same error. Since it's technically a "lookup" field, does the code have to be different/

I feel like I'm so close I can taste it, lol...

Thoughts?
Clay AshworthClay Ashworth
If the Account_Name__c field is a lookup then remove line 25 altogether. The field value will be set with the b.Account_Name__c= a.Id; statement. If you try to assgn a text value to an ID field the system will balk at you (which it did).

If this works out please mark this as solved :-)
econ242econ242
It worked!!!  :)

Only thing now is it shows as passed, but when I look at the trigger it shows 66% code coverage (2/3)... i need to find out where it is failing now. Do i use the Developer Console for that?

Clay AshworthClay Ashworth
Yes, the developer console has tools that will allow you to run the test class from there and then view the results for each trigger. It will show you by highlighting in red lines in the trigger that are not covered. Below is a link that will give you instructions on how to do this.

https://help.salesforce.com/HTViewHelpDoc?id=code_dev_console_tab_tests.htm&language=en_US
econ242econ242
Strangely...it's the last line that is showing up in red (bolded below):

trigger BusinessRatingChange on Account (before update)
{
    for(Account a:Trigger.new)
    {
        if(system.trigger.OldMap.get(a.Id).Business_Rating__c != system.trigger.NewMap.get(a.Id).Business_Rating__c)
        {
            a.Business_Rating_History__c = a.Business_Rating__c;
            }
            }
            }

I'm trying a couple things, but nothing yet...will keep at it...
Clay AshworthClay Ashworth
Inserting the New_Referrals_Received__c b is only setting the value. Change the value on the object after the insert and then do an update b so that the value will change and fire the trigger.
econ242econ242
Let me ask you this...just so I can make sure I understand...

In the test class, when we create the "Test Econ Change" Account and set the a.RS_of_New_WC_Clients_Per_Month__c = 10, that should set the value in that field on the test Account (RS_of_New_WC_Clients_Per_Month__c) to 10, which in turn should fire my formula field "Business_Rating__c" and make that field value for Business_Rating__c, a "C".

Reason being is the "Business_Rating__c" is determined from the RS_of_New_WC_Clients_Per_Month__c field and another field - Actual_Referrals_Month_W_C__c...that calculates and creates the "C" rating. So now when we insert the new referral record in the test class (Referral_s_Received__c b = new Referral_s_Received__c () i.e., "insert b" in the code...it should update the test Account, yes?

The value before the "insert b" action in the Actual_Referrals_Month_W_C__c is a zero - which gives the initial business rating of "C", but after the "b" insert, the "Actual_Referrals_Month_W_C__c" should change to 8 per the test class entry...the trigger should fire when that changes from a "0" to an "8"...

So now I'm a bit lost since you had mentioned in the test class - "//  If the insertion of b will cause the field to update on the account record you should not need to update the account"...

I sincerly apologize for so much back and forth...you've been so helpful with this...I feel like I'm missing something so easy...
Clay AshworthClay Ashworth
if the line "a.Business_Rating_History__c = a.Business_Rating__c;" is not being covered that means "if(system.trigger.OldMap.get(a.Id).Business_Rating__c != system.trigger.NewMap.get(a.Id).Business_Rating__c)" is not evaluating to true.

Are the formula fields working as expected with the trigger in your sandbox? If yes you will want to add a System.Assert in your test class after you insert b to check that the Business_Rating__c field on the account was updated properly.

econ242econ242
They are working in the Sandbox perfectly...

So would I use a System.AssertEquals(Business_Rating__c)? I'm also wondering if "beforeupdate" is the correct choice for this trigger...
Clay AshworthClay Ashworth
Since you are modifying a field on the object called out in the trigger it has to be a Before trigger. After triggers cannot modify fields on the object for which they are firing on.


You did mention that there are two fields driving the value of the Business_Rating__c field. One being the RS_of_New_WC_Clients_Per_Month__c for which a value was set in the test class before we inserted a. The second you mentioned is Actual_Referrals_Month_W_C__c that should have a value of 0 upon insert of a and then change to 8 once b is inserted.

I added in several system.Assert statements so that we can test where the update is not happening. For simple triggers and test classes you can usually skip these but for classes with multiple methods or those where you have some other trigger, class, workflow or formula field that needs to occur first these are helpful as they will not allow the code to progress if the expected results are not met.

//Annotate class as test
    @IsTest
    
    //Name the class
    private class AccountBeforeUpdateTest {
        
        //name the test method
        static testMethod void businesratingtest() {
    
        //Create and insert records for test
        Account a = new Account();
            a.Name='Test';
            a.RS_of_New_WC_Clients_Per_Month__c = '10';
        
        insert a;

	//verify that expected values for fields on the account were updated
	system.AssertEquals(a.Actual_Referrals_Month_W_C__c == 0);
	System.AssertEquals(a.Business_Rating__c == "C");
    
        //Create and Insert new referral record for test
        New_Referrals_Received__c b = new New_Referrals_Received__c>();
        b.Name='August 1, 2014';
        b.of_Referrals_Received__c=6;
        b.Case_Type__c='Workers Comp';
        b.Date__c=Date.newInstance(2014,08,01);
//relate b to the Account
        b.Account_Name__c = a.Id;
          
        insert b;

	//check to make sure b inserted correctly
	system.AssertEquals(b.Id != null);

	//verify that expected values for fields on the account were updated
	system.AssertEquals(a.Actual_Referrals_Month_W_C__c == 8);
	System.AssertEquals(a.Business_Rating__c == “expected” value);

	
    
        }
    
    }



I suspect that there is one detail that we are missing here and it is most likely attributable to  to what is happening between the insert of the a and b records since the "if(system.trigger.OldMap.get(a.Id).Business_Rating__c != system.trigger.NewMap.get(a.Id).Business_Rating__c)" is not evaluating to true with the test class but is working in the sandbox environment. This is one of the primary drivers behind requiring code coverage on triggers; to understand EXACTLY what your code is doing.

Sorry this seems to be taking so long but we will figure it out :-)