+ Start a Discussion
tinman44tinman44 

Unit Tests??? I am Lost...

Hello all-

i am trying to write my first Apex trigger and deploy it to a production org and am missing something. I have written the Trigger to do what I want but cannot see m to figure out how to write the unit tests for this and then deploy it to an org. I have gone over the documentation all day and still am hitting my head against a wall. Wondering if anyone would be so kind as to help me understand what the unit tests for a simple script like this would look like? I would be very greatful...or at least point me in the direction of a simple hello world example?

Code:
Code:
trigger UpdateOpportunityLastActivityDate on Event (before insert, before update) {

for (Event e : Trigger.new) {
 
     Opportunity[] opp = [select id from opportunity where id= :e.WhatId];
     opp[0].Next_Activity_Date__c = e.ActivityDate;
     update opp;
     
    }

}


 
Thanks in advance for any help/direction.

JonPJonP
Tinman44,

To unit test your Apex Trigger, you need to create an Apex Class containing test methods (using the TestMethod keyword) which exercise your trigger code.  In this case, you would need test methods that insert and update Events.

When you deploy your Apex to production, the system will execute the test methods in your Apex class, and those test methods will cause your Apex trigger to be invoked, and that will count toward the minimum code coverage requirements.

But remember that the reason to create unit tests is not to get past our code coverage checker, but to actually make sure your code works as expected before exposing your end users to it.  So when you write your Event test methods, you should think about different scenarios that would cause your trigger to fire and make sure you handle them.

For instance:

- Your code assumes that every Event.WhatId is an Opportunity Id.  What if a user creates or updates an Event associated with an Account or other object? 

- You perform a query and update for each Event in the loop.  This works fine with a few Events, but what if someone batch-imports a large set of Events?  Create a unit test that inserts or updates a large number of Events.

(You may want to use one loop to generate the set of Opportunity Ids, then execute a single query in which you retrieve all those Opportunities, and finally a single update statement to push your changes into the database in one transaction.  This is far more efficient and will avoid the Apex governor limits.)

The developer.force.com wiki contains a number of excellent design patterns for addressing these scenarios, such as:

http://wiki.apexdevnet.com/index.php/BESTMassUpdateContactsOnAccount.apex


As you write more and more unit tests, you'll get better at identifying positive and negative scenarios, which will improve the quality of your code as you write it, and your confidence in it after you deploy it to production.

Jon


Message Edited by JonP on 01-28-2008 02:57 PM
tinman44tinman44

Thanks for the help! I was able to get it up and running last night and deployed to a prod org via Eclipse.

Time for a beer...

Paul80304Paul80304
I too find myself stuggling with the Unit Test requirements.  I'm working off the basic 'Hello World' example that ships with the new, 3.3 based Eclipse Force.com DE.  I've managed to construct a unit test method for "MyHelloWorld" class, but it doesn't appear to be exercising the actual trigger event.  All the trigger does is call/create an instance of my class, so how do I test the actual trigger?

Here's the code (note I modified the Hello World example to update a Lead vs. an Account):

helloWorldLeadTrigger

trigger helloWorldLeadTrigger on Lead (before update) {

    MyHelloWorld.addHelloWorld(Trigger.new);
}  


MyHelloWorld.cls

public class MyHelloWorld {
     
    public static void addHelloWorld(Lead[] leads){
        for (Lead aLead:leads){
            if (aLead.Hello__c != 'World')
            aLead.Hello__c = 'World';
        }
    }
   
    static testMethod void myTest() {
       
        System.Debug('');
        System.Debug('Unit Test: Paul Hello World');
        List<Lead> LeadToUpdate = new List<Lead>();
        Lead myLead = new Lead();
        myLead = [select id, Hello__c from Lead where lastname = 'Nugent'];
        LeadToUpdate.add(myLead);
        MyHelloWorld.addHelloWorld(LeadToUpdate);
       
    }
   
}

Outputlog From Apex Code Test Runner:

helloWorldLeadTrigger:

    Test coverage of selected Apex Trigger is 0%, at least 1% test coverage is required

General:

    Test coverage of selected Apex Trigger is 60%, at least 75% test coverage is required

---

How do I bump up my %'s...especially on the trigger code itself? 

Thanks for the help!
-P


JonPJonP
You're really close.  In your test method, just replace:

    MyHelloWorld.addHelloWorld(LeadToUpdate);

with:

    update LeadToUpdate;

This will cause an update event, firing your trigger, and in turn calling your MyHelloWorld::addHelloWorld() method.

To increase your code coverage further, include a second lead in your update set, having Lead.Hello__c = 'World'.  Then you'll have tested both branches of the if statement.

With these changes, your code coverage will look great, but are your tests actually accomplishing anything?  This is just a Hello, World example, but consider going all the way and adding System.assert() statements to your test method.

For example, after the update statement in your test method, why not add:

    for (Lead afterUpdateLead = [select id, Hello__c from Lead where lastName = 'Nugent']) {
        System.assertEquals('World', afterUpdateLead.Hello__c);
    }
Paul80304Paul80304
JonP,

Thanks a million for getting me over the hump!  These nuggets of advice you provided are exactly what is missing from the documentaton.  Obviously the 'Hello World' code is pretty worthless, but the overall concepts are now complete with your response, so I can't thank you enough.

Happy Coding,
-Paul
mba75mba75
HI Jon

Do you have an idea to solve the following issue ?


I try to deploy my job but I get only 17 %test coverage.
 
I use the following opportunity trigger after insert to update the opportunitylineitem record of a specifique opportunity that code works perfectly with the clone button:

trigger opportunity_after_insert on Opportunity (after insert) {

Opportunity[] opps=Trigger.new;
opportunity_clone.Opp_line_item_del(opps);
}


 This trigger call opportunity_clone.Opp_line_item_del

public class Opportunity_clone {
public static void Opp_line_item_del(Opportunity[] opps){

List<OpportunityLineItem> updatedoli = new List<OpportunityLineItem>();


for (OpportunityLineItem oli : [SELECT Quote_Reference__c,ServiceDate,Provisioning_Type__c, Document_Type__c, Non_Verified_Contract__c,FROM OpportunityLineItem WHERE Opportunityid in :opps]) {
oli.Quote_Reference__c= null;
oli.ServiceDate= null;
oli.Provisioning_Type__c= null;
oli.Document_Type__c= null;
oli.Non_Verified_Contract__c= False;

updatedoli.add(oli);
}

update updatedoli;
}

}
 

 
My Issue is  the test method does not execute the iteration on the opportunity line item or execute the trigger before the opportunity line are insert .

but with my test method :

insert the opportunity then call my trigger ( when there is no opportunitylineitem ) and then it insert the opportunity line item .

So test covarage result is 17 %
 
Do you have any suggestion .
 
public class unit_test_opp_clone{

static testmethod void opp_clone_oli_test(){
...

//Create opportunity
Opportunity o = new Opportunity (name='unitest',Pricebook2id=pb.id, Stagename='Initial Proposal Submitted', Closedate=Date.newInstance(2008,02,02));
insert o;

//Create opportunityline item
OpportunityLineItem[] Oli=new OpportunityLineItem[0];

oli.add(new OpportunityLineItem(Opportunityid=o.id,pricebookentryid=pbe.id,Quantity=1 ,UnitPrice=1000 , Frequency__c='Monthly',Billing_Term__c='12',Contract_Re_sign__c='No'));

oli.add(new OpportunityLineItem(Opportunityid=o.id,pricebookentryid=pbe.id,Quantity=1 ,UnitPrice=3000 , Frequency__c='Monthly',Billing_Term__c='12',Contract_Re_sign__c='No'));
insert oli;

opportunity opp_clone= o.clone(False) ;

List<OpportunityLineItem> updatedoli = o.OpportunityLineItems.deepclone(false);
for (OpportunityLineItem oli_C : updatedoli) {
oli_C.OpportunityId = opp_clone.Id;

updatedoli.add(oli_C);

update updatedoli ;
insert opp_clone ;


for (OpportunityLineItem ol : [SELECT Quote_Reference__c,ServiceDate,Provisioning_Type__c, Document_Type__c, Non_Verified_Contract__c FROM OpportunityLineItem WHERE Opportunityid = :opp_clone.id]) {
system.assertEquals(null,ol.Quote_Reference__c);
system.assertEquals(null,ol.ServiceDate);
system.assertEquals(null,ol.Provisioning_Type__c);
system.assertEquals('Order Form',ol.Document_Type__c);
system.assertEquals(False,ol.Non_Verified_Contract__c);
}

}
}
 
 
I will appreciate if you can give me some help on that case.

Ron HessRon Hess
Can you write /modify a test method that does not rely on or attempt to force the trigger to call the class,
but rather just constructs an opp, with line items, then call the class itself, directly with the opp in an array

that should get your code coverage in the class to 100%


so, add (something like)
Code:
opportunity_clone.Opp_line_item_del( new Opportunity[]{ opp_clone } );

 
to your testMethod, then the class you are trying to test will get passed an array containing an opp, with the line items.



Message Edited by Ron Hess on 02-06-2008 11:31 PM
mba75mba75
Thank you Ron It works perfectly

 
tinman44tinman44

Thanks for those who have replied. I have found this very helpful. However I have run into another issue. I have a block of my code that is a big if/then statement to turn a Month value froma  picklist into a integer so I can set a date object. I cannot reach 75% coverage of my code becuase the length of this if/then statement. Try as I may I cannot write a unit test for it. Wondering if anyone could point me in the right direction. Here is a snippet of the code that is preventing me from getting 75%.

Code:

Integer Month = 01;

if(JanSD.Inactive_this_month__c == 'Jan') {
        
  Month = 01;     
} else  if(JanSD.Inactive_this_month__c == 'Feb'){
       
  Month = 02; 
       
} else  if(JanSD.Inactive_this_month__c == 'Mar'){
        
 Month = 03;
}

... I iterate ofer the 12 months...

 

String YearStrng = JanSD.Inactivate_this_year__c;

Integer Year = Integer.valueOf(YearStrng);

Integer Day = 01;

Date deactivateDate = Date.newInstance(Year,Month,Day);

 

 
Thanks in advance for any help!