0

I am pretty new to Apex triggers and Apex Class Tests but i have written and deployed a couple successfully so far. But i am having real problems with a new trigger that i have created.

I have created a trigger that will create Assets (in a custom object (Don't ask!)) when an opportunity is closed Won. The trigger works in sandbox and has 100% Code Coverage with the Test in sandbox.

But when i try and deploy to production i get the following error:

Code Coverage Failure The following triggers have 0% code coverage. Each trigger must have at least 1% code coverage. CreateAssetonClosedWon

Do you have any idea what would cause this to happen?

Trigger below:

trigger CreateAssetonClosedWon on Opportunity (after insert, after update) {
     for(Opportunity o: trigger.new){ 
      if(o.isWon == true && o.HasOpportunityLineItem == true){
         String opptyId = o.Id;
         OpportunityLineItem[] OLI = [Select UnitPrice, Quantity, PricebookEntry.Product2Id, PricebookEntry.Product2.Name, Description, Converted_to_Asset__c, Module__c, Product_Family__c
                                      From OpportunityLineItem 
                                      where OpportunityId = :opptyId  and Converted_to_Asset__c = false and Product_Family__c <> 'Support and Maintenance' and Product_Family__c <> 'Professional Services' and Product_Family__c <> 'Discount'];
         Asset_Custom__c[] ast = new Asset_Custom__c[]{};
         Asset_Custom__c a = new Asset_Custom__c();
         for(OpportunityLineItem ol: OLI){
            a = new Asset_Custom__c();
            a.Account__c = o.AccountId;
            a.Product__c = ol.PricebookEntry.Product2Id;
            a.Sale_Price__c =  ol.UnitPrice;
            a.Quantity__c = ol.Quantity;
            a.Purchase_Date__c = o.CloseDate;
            a.Install_date__c = o.CloseDate;
            a.Active_Cancelled__c = 'Active';
           // a.Description = ol.Description;
           // a.Name = ol.PricebookEntry.Product2.Name;
           // a.Module__c = ol.Module__c;
            ast.add(a);
            ol.Converted_to_Asset__c = true;
       }
      update OLI; 
      insert ast;
     }
    }
}

Test:

@isTest(seealldata=true)


private class testCreateAssetonClosedWon {

  static testMethod void testCreateAssetonClosedWon(){

    Account a = [select Id from Account limit 1];
    PricebookEntry pbID = [select Id from PricebookEntry WHERE IsActive = TRUE limit 1];
    Opportunity o = new Opportunity();
    OpportunityLineItem ol = new OpportunityLineItem ();
    Contact c = [select Id from Contact Limit 1];
    Id nodemo = Schema.SObjectType.Opportunity.getRecordTypeInfosByName().get('No Demo').getRecordTypeId();


    o.RecordTypeId = nodemo;
    o.AccountId = a.Id;
    o.Primary_Contact__c = c.id;
    o.Name = 'test';
    o.StageName = 'Opportunity Started';
    o.CloseDate = date.today();
    o.type = 'Quote';
    o.LeadSource= 'Cold Call';
    insert o;

    ol.OpportunityId = o.Id;
    ol.Quantity = 1;
    ol.UnitPrice = 2.00;
    ol.PricebookEntryId = pbId.Id;

    insert ol;

    o.StageName= 'Closed Won';
    update o;

    delete ol;
    delete o;



  }


}

Any help with this would be really appreciated.

  • 4
    You shouldn't use the (seealldata=true) annotation. The problem might be that your production org doesn't include any account, contact or pricebookEntry. You shoud insert your test data inside your test. – Martin Lezer Sep 5 '17 at 9:00
  • If i remove (seealldata=true) the test fails in the sandbox. – Benjy Hunt Sep 5 '17 at 9:03
  • Of course it does. This is because your test is badly written as I said before. – Martin Lezer Sep 5 '17 at 9:04
  • Ok, So please can you give me some guidance on what needs to be changed, Please can you provide a bit more information on what you mean by "You shoud insert your test data inside your test."? – Benjy Hunt Sep 5 '17 at 9:08
  • Just insert a new account, a new contact, a new pricebookentry before inserting your opportunity. – Martin Lezer Sep 5 '17 at 9:10
2

Few things in your code that you must keep in practice:

  • Avoid (seealldata = true) in test class
  • Avoid Queries and DML's inside Loops
  • Try using Maps
  • Avoid initializing variables inside loop

I believe creating an OpportunityLineItem in Test class is a bit of headache. How to Create Opportunity Line Items in Test Classes --> Follow This Link for creating line item in test class. This helps me always.

   trigger CreateAssetonClosedWon on Opportunity (after insert, after update) {
    Map<Id, Opportunity> OpportyMap = new Map<Id, Opportunity>();
    List<Asset_Custom__c> ast = new List<Asset_Custom__c>();
    Asset_Custom__c a = new Asset_Custom__c();
    for(Opportunity o: trigger.new){ 
        if(o.isWon == true && o.HasOpportunityLineItem == true){
            OpportyMap.put(o.Id,o);
        }
    }

    List<OpportunityLineItem> OLI = new List<OpportunityLineItem>([Select UnitPrice, Quantity, PricebookEntry.Product2Id, PricebookEntry.Product2.Name, Description, Converted_to_Asset__c, Product_Family__c, OpportunityId From OpportunityLineItem  where  Converted_to_Asset__c = false and Product_Family__c != 'Support and Maintenance' and Product_Family__c  != 'Professional Services' and Product_Family__c != 'Discount' and OpportunityId In :OpportyMap.Keyset()]);

    for(OpportunityLineItem ol: OLI){
        a.Account__c = OpportyMap.get(ol.OpportunityId).AccountId;
        a.Product__c = ol.PricebookEntry.Product2Id;
        a.Sale_Price__c =  ol.UnitPrice;
        a.Quantity__c = ol.Quantity;
        a.Purchase_Date__c = OpportyMap.get(ol.OpportunityId).CloseDate;
        a.Install_date__c = OpportyMap.get(ol.OpportunityId).CloseDate;
        a.Active_Cancelled__c = 'Active';
        // a.Description = ol.Description;
        // a.Name = ol.PricebookEntry.Product2.Name;
        // a.Module__c = ol.Module__c;
        ast.add(a);
        ol.Converted_to_Asset__c = true;
    }

    update OLI; 
    insert ast;

}

And the test Class

 @isTest
private class testCreateAssetonClosedWon {

    static testMethod void testCreateAssetonClosedWon(){

        Account a =new Account(Name = 'New Test Account');
        Opportunity o = new Opportunity();    
        Contact c = new Contact(LastName = 'New Contact');
        Id nodemo = Schema.SObjectType.Opportunity.getRecordTypeInfosByName().get('No Demo').getRecordTypeId();
        o.RecordTypeId = nodemo;
        o.AccountId = a.Id;
        o.Primary_Contact__c = c.id;
        o.Name = 'test';
        o.StageName = 'Opportunity Started';
        o.CloseDate = date.today();
        o.type = 'Quote';
        o.LeadSource= 'Cold Call';
        insert o;

        Product2 prod = new Product2(
            Name = 'Product X',
            ProductCode = 'Pro-X',
            isActive = true
        );
        insert prod;
        Id pricebookId = Test.getStandardPricebookId();
        //Create your pricebook entry
        PricebookEntry pbEntry = new PricebookEntry(
            Pricebook2Id = pricebookId,
            Product2Id = prod.Id,
            UnitPrice = 100.00,
            IsActive = true
        );
        insert pbEntry;

        //create your opportunity line item.  This assumes you already have an opportunity created, called opp
        OpportunityLineItem oli = new OpportunityLineItem(
            OpportunityId = o.Id,
            Quantity = 5,
            PricebookEntryId = pbEntry.Id,
            TotalPrice = 5 * pbEntry.UnitPrice
        );
        insert oli;


        o.StageName= 'Closed Won';
        update o;

    }


}
  • 1
    It is a better practice to create a test data factory class and call that from all test methods, but creating in the test class is much better than using seeAllData. – Dave Humm Sep 5 '17 at 10:48
  • 1
    Yes @DaveHumm, This is just one solution to the code. I prefer TestDataFactory in case i have plenty of Triggers. But this can be used as a unified approach – Saumya Gaikwad Sep 5 '17 at 10:51
  • Why the suggestion to use list<> instead of [] – Eric Sep 5 '17 at 12:24
  • @Eric lists are elastic and can grow as needed provided that you use the List add method to add new elements follow the link -> salesforce.stackexchange.com/questions/5447/… – Saumya Gaikwad Sep 5 '17 at 12:27
  • You do know that [] is a list in apex right? List<account> is the same as account[] – Eric Sep 5 '17 at 12:29

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.