You need to sign in to do that
Don't have an account?
InsertWittyName
Unit Testing Woes
Hi folks.
I have an Apex Trigger that I'm trying to write unit tests for.
The problem is I'm unsure how to test the following:
Opportunity newOpp = oldOpp.clone(false);
insert newOpp;
OpportunityLineItem oli = product.clone(false);
oli.OpportunityId = newOpp.Id;
oli.Objection__c = '';
oli.Outcome__c = '';
newProducts.add(oli);
Every line is marked as not tested and because of this my unit test fails.
Any advice?
Many thanks.
Is the code snippet part of the trigger or test method? Can you post the full trigger and the test method? I will be happy to look at them.
Hi John.
Many thanks for replying.
/* Any opps with Products that have an Outcome (Outcome__c) or Product Outcome (Objection__c) of 'Move to New Opp'. For each of these Products, Clone the existing opp (pre update) and add the Product. Reset the Outcome & Product Outcome. */ trigger tgrOppCreateNewOppsWithProducts on Opportunity (before update) { // Only fire on updates if (Trigger.isUpdate) { List <OpportunityLineItem> products = new List<OpportunityLineItem>(); List <OpportunityLineItem> newProducts = new List<OpportunityLineItem>(); List <Opportunity> opps = new List<Opportunity>(); List <Opportunity> newOpps = new List<Opportunity>(); List <Opportunity> oldOpps = new List<Opportunity>(); // Go through each updated record for (Integer i = 0; i < trigger.new.size(); i++) { Opportunity opp = trigger.new[i]; Opportunity oldOpp = trigger.old[i]; if(opp.HasOpportunityLineItem && opp.isClosed && !oldOpp.isClosed) { opps.add(opp); oldOpps.add(oldOpp); } } if(opps.size() > 0) { products = [Select Id, OpportunityId, PricebookEntryId, Quantity, Objection__c, Outcome__C from OpportunityLineItem where OpportunityId in :opps]; } for (Integer i = 0; i < opps.size(); i++) { Opportunity oldOpp = oldOpps[i]; for (OpportunityLineItem product : products) { // Check 'Product Outcome' and 'Outcome' if (product.Objection__c == 'Move to New Oppy' || product.Outcome__c == 'Move to New Opp') { // Clone pre-update opp. Opportunity newOpp = oldOpp.clone(false); newOpps.add(newOpp); // Clone Product, point to newOpp and reset outcomes OpportunityLineItem oli = product.clone(false); oli.Objection__c = ''; oli.Outcome__c = ''; newProducts.add(oli); } } } // Add the new Opps and Products if(newProducts.size() > 0) { insert newOpps; for(Integer i = 0; i < newProducts.size(); i++) { newProducts[i].OpportunityId = newOpps[i].Id; } insert newProducts; } } }
Test:
public class testTgrOppCreateNewOppsWithProducts { static testMethod void test() { //Create an Account Account account = new Account(name='Test Account 1'); insert account; //Create an Opportunity Opportunity opp = new Opportunity (name = 'Test Opp1', type = 'New Business', business_segment__c = 'AM100', stagename = 'New 0%', closedate = system.today(), ForecastCategoryName = 'Pipeline', accountid = account.id); insert opp; System.assert(opp != null); //Create a Product Product2 p = new Product2 (name = 'Some Product', Description = 'desc'); insert p; //Get standard pricebook Pricebook2 stdPb = [select Id from Pricebook2 where isStandard = true limit 1]; //Create pricebook entry PricebookEntry pbe = new PricebookEntry (pricebook2id = stdPb.id, product2id = p.id, unitprice = 1.0, isActive = true); insert pbe; //Insert opportunity line item OpportunityLineItem oli = new OpportunityLineItem (OpportunityId = opp.Id, PricebookEntryId = pbe.Id, Quantity = 1, UnitPrice = 1); insert oli; OpportunityLineItem oli2 = new OpportunityLineItem (OpportunityId = opp.Id, PricebookEntryId = pbe.Id, Quantity = 2, UnitPrice = 2); insert oli2; // Update oli, setting Outcome__c/Objection__c to Move to New Opp (fire the trigger) oli.Objection__c = 'Move to New Opp'; oli.Outcome__c = 'Move to New Opp'; update oli; System.assert(oli != null); System.assertEquals(oli.Objection__c, 'Move to New Opp'); System.assertEquals(oli.Outcome__c, 'Move to New Opp'); // Now change to closed won opp.StageName = 'Closed Won'; opp.Reason_Closed_Won__c = 'Price'; update opp; System.assert(opp != null); } }
What is have found is that when creating a trigger, put all the code in a subordanate apex class makes things a lot easier and simplifies with code reuse.
Thus
/* trigger */
trigger tgrOppCreateNewOppsWithProducts on Opportunity (after insert, before update) { tr_OppCreateNewOppsWithProducts opCreate = new tr_OppCreateNewOppsWithProducts (Trigger.New,trigger.IsUpdate,trigger.IsInsert); opCreate.DoTrigger(); return; } // end trigger public class tr_OppCreateNewOppsWithProducts { public class My1Exception extends Exception {} boolean IsUpdate = false; boolean IsInsert = false; boolean IsAfter = false; list <Opportunity> CVs ; public tr_OppCreateNewOppsWithProducts () {} public tr_OppCreateNewOppsWithProducts (list<Opportunity xCVs, boolean xisUpdate,boolean xisInsert) { CVs = xCVs; this.IsUpdate=xIsUpdate; this.IsInsert=xisInsert; } public boolean DoTrigger() { etc.... } }
Thus all your code is in a standard class which you can then build test cases for without having to force a trigger being fired.
Bruce
I tried your code and was able to achieve 100% code coverage with a couple of minor changes:
1. You don't need the if(Trigger.isUpdate), since the trigger is only using the "after Update" action.
2. When I tried running your code, the trigger was throwing an exeception, the "TotalPrice" is a required field on products, I see that you are cloning the OpportunityLineItems but your "select" statement did not include TotalPrice. I changed line 24 to include TotalPrice:
products = [Select Id, OpportunityId, PricebookEntryId, Quantity, TotalPrice, Objection__c, Outcome__C from OpportunityLineItem where OpportunityId in :opps];
3. You should use a deep clone for lines 37 and 41 of your code:
Opportunity newOpp = oldOpp.clone(false, true); OpportunityLineItem oli = product.clone(false, true);