5

New to Apex here. I cobbled together a trigger that creates a new task whenever a certain type of opportunity is Closed - Lost, but I can't figure out how to get ANY coverage with a unit test. Does anyone see what I am doing wrong here?

Trigger:

trigger OpportunityAfter on Opportunity (after update){

Set AccountIDs = new Set();

for(Opportunity o : trigger.new){
     AccountIDs.add(o.AccountId);
}

for(Opportunity opp : trigger.new)
if(opp.StageName == 'Closed - Lost' && trigger.oldMap.get(opp.Id).StageName != opp.StageName)
AccountIds.add(opp.AccountID);

Task[] tasksToInsert = new Task[]{};

for (Account acc : [Select Id, Name, OwnerId from Account where Id IN :AccountIds]) 
for (Opportunity opp : trigger.new)
if(opp.StageName == 'Closed - Lost' && opp.Business_Line__c == 'Workforce' && opp.Survey_Type__c != 'APulse' && trigger.oldMap.get(opp.Id).StageName != opp.StageName)
tasksToInsert.add(new Task(WhatId=acc.Id, OwnerId=opp.OwnerId, Subject='Follow-up: ' + opp.name + ' - ' + acc.name, ActivityDate=(opp.CloseDate + 180), Description='Follow up to see how engagement initiatives are going and if Avatar can help. Refer to lost opportunity: ' + opp.name));

if(tasksToInsert != null && !tasksToInsert.isEmpty())
Database.insert(tasksToInsert);

}

Test:

@isTest
public class TestClass {

    static testMethod void myUnitTest() {

    //Set up user
    User u1 = [SELECT Id FROM User WHERE Email='[email protected]'];

    //Run As U1
    System.RunAs(u1){

    System.debug('Testing trigger... (single record validation)');

    //NEW Account record
    Account acc = new account();
    acc.Name = 'Test Account';
    acc.Industry = 'Healthcare';

    insert acc;

    //NEW Opportunity record
    Opportunity opp = new Opportunity();
    opp.Name = 'Test Opportunity'; 
    opp.CloseDate = System.today();
    opp.StageName = 'S1 - Investigative';
    opp.Type = 'Repeat Business - Wrkforce HC';
    opp.AccountId = acc.Id;
    opp.Survey_Type__c = 'Product Name';

    insert opp;

    opp.StageName = 'Closed - Lost';
    opp.Reason_Won_Lost__c = 'Relationship';

    update opp;

    //NEW Task record   
    Task testTask = new task();
    testTask.WhatId = acc.Id;
    testTask.Subject = 'Follow-up: ' + opp.name + ' - ' + acc.name;
    testTask.ActivityDate = (opp.CloseDate + 180); 
    testTask.Description = 'Follow up to see how engagement initiatives are going and if we can help. Refer to lost opportunity: ' + opp.name;

    insert testTask;

    //Validate single insert
    System.assertEquals(acc.Id, testTask.WhatId);

    }
    }
}
2
  • 1
    so you mean the test class executes without any error but does not cover a even a single line from the trigger on the opportunity object? Apr 15, 2015 at 13:25
  • Exactly - the trigger works in the sandbox and the test class executed without any error but does not cover even a single line. I modified the test class to reflect Suri's suggestion below, but now I get the error I describe in my comment below the answer.
    – Greg
    Apr 15, 2015 at 22:57

3 Answers 3

3

The trigger code is having lot of redundancy, So I modified it. before posting the modified code few lines of explanation what I changed. you collected account Ids twice and did the condition check twice where once is more enough. I am also made changes to the test class, where Task data is provided which is not required because the trigger has to create it once the necessary conditions are met. I am expecting this trigger should work for you, if there is any errors feel free to post as a comment.

trigger OpportunityAfter on Opportunity (after update){

    Set AccountIDs = new Set();

    for(Opportunity o : trigger.new){
        if(opp.StageName == 'Closed - Lost' && opp.Business_Line__c == 'Workforce' && 
            opp.Survey_Type__c != 'APulse' && trigger.oldMap.get(opp.Id).StageName != opp.StageName){

                AccountIDs.add(o.AccountId);
         }
    }

    Task[] tasksToInsert = new Task[]{};

    for (Account acc : [Select Id, Name, OwnerId from Account where Id IN :AccountIds]){
        for (Opportunity opp : trigger.new){
            if(opp.AccountId == acc.Id){
                Task tas = new Task();
                tas.WhatId=acc.Id;
                tas.OwnerId=opp.OwnerId;
                tas.Subject='Follow-up: ' + opp.name + ' - ' + acc.name;
                tas.ActivityDate=(opp.CloseDate + 180);
                tas.Description='Follow up to see how engagement initiatives are going and if Avatar can help. Refer to lost opportunity: ' + opp.name;
                tasksToInsert.add(tas);                                                         
            }
        }
    }
    if(tasksToInsert != null && !tasksToInsert.isEmpty())
    Database.insert(tasksToInsert);

}

@isTest
public class TestClass {

    static testMethod void myUnitTest() {

    //Set up user
    User u1 = [SELECT Id FROM User WHERE Email='[email protected]'];
    test.startTest();  
    //Run As U1
    System.RunAs(u1){

    System.debug('Testing trigger... (single record validation)');

    //NEW Account record
    Account acc = new account();
    acc.Name = 'Test Account';
    acc.Industry = 'Healthcare';

    insert acc;

    //NEW Opportunity record
    Opportunity opp = new Opportunity();
    opp.Name = 'Test Opportunity'; 
    opp.CloseDate = System.today();
    opp.StageName = 'S1 - Investigative';
    opp.Type = 'Repeat Business - Wrkforce HC';
    opp.AccountId = acc.Id;
    opp.Survey_Type__c = 'Product Name';

    insert opp;

    opp.StageName = 'Closed - Lost';
    opp.Reason_Won_Lost__c = 'Relationship';
    opp.Business_Line__c = 'Workforce'  //added this line to meet the if condition while collecting the set of id in the trigger.

    update opp;
   test.stopTest();
    // Here I am expecting the task has been inserted and doing a query for it         and check it in the assert statement.
   Task  testTask=[Select Id, WhatId from Task where WhatId=:acc.Id];
    //Validate single insert
    System.assertEquals(acc.Id, testTask.WhatId);

    }
    }
}
4
  • I made your suggested changes to the trigger, but modified the third line to read: Set<ID> AccountIds = new Set<ID>(); the trigger works in sandbox, but the test errors out. I get "list has no rows for assignment to SObject" on the line: Task testTask = [Select Id, Subject from Task where WhatId=:acc.Id]; any thoughts?
    – Greg
    Apr 15, 2015 at 22:54
  • Hi Suri, do you have any additional thoughts in light of my comment? The test does not work, unfortunately.
    – Greg
    Apr 16, 2015 at 22:31
  • I added a line in the test class, now it should work. Greg when you run test class in the Dev Console you may see the colored portions on the test coverage part, if there is no color on the code it is not covered so please let me know if is not get colored and what line it is. This easy way to identify test coverage over code. If this answered your question make this as solution.
    – Suresh
    Apr 17, 2015 at 13:45
  • Thanks Suri. Your edit helped me uncover some validation issues I was having with the test. All works now. MUCH appreciated!
    – Greg
    Apr 20, 2015 at 18:28
0

Unit Test is Providing 0% Coverage for Apex Trigger


trigger Splittrigger_newclass on Indexing__c (before delete,after insert) { list lstvoyage=new list(); list q = new list(); list p = new list();

   //To store parent ids
list<id> IndIds=new list<id>();
for(Indexing__c Ind:trigger.old)
{
    IndIds.add(Ind.id);
    system.debug('inids>>>'+IndIds);
}  
//Collecting all child records related to Parent records
list<C_Voyage__c> listVoyage=[select id,main_case_id__c from C_Voyage__c where Indexing__c in :IndIds];
system.debug('listVoyage'+listVoyage.size());
//deleting child records
delete listVoyage;

for(Indexing__c y:trigger.new)
{
    system.debug('INdex>>>>>'+y);
    if(y.Voyage_Created_Ref__c !=null)
    {
p=y.Voyage_Created_Ref__c.split(',');
for (Integer i = 0; i < p.size();i++) {
    C_Voyage__c tst = new C_Voyage__c();
    tst.Voyage_Ref__c = p[i];
    tst.Voyage_Type__c = 'Created';
    tst.Indexing__c= y.Id;
    tst.main_case_id__c= y.Case_Indexing_Ref__c;
    lstvoyage.add(tst);
    system.debug('listofvoyage>>>>>'+lstvoyage);
}

upsert lstvoyage; if(y.Voyage_Amended_Ref__c !=null) {

q=y.Voyage_Amended_Ref__c.split(',');

for (Integer j = 0; j < q.size();j++) {
    C_Voyage__c atst = new C_Voyage__c();
    atst.Voyage_Ref__c = q[j];
    atst.Voyage_Type__c = 'Amended';
    atst.Indexing__c= y.Id;
    atst.main_case_id__c= y.Case_Indexing_Ref__c;
    lstvoyage.add(atst);
}

upsert lstvoyage; } } } }

-1

You're not getting code coverage because you aren't starting your test. This means executing your test method through the console or a deployment just builds the data but never checks it. You would need test.start() after you've build you test data but before you execute the dml to run the trigger. You will also need a test.stop() after you've finished running your test - this tells it to end the test.

@isTest
public class TestClass {

  static testMethod void myUnitTest() {

    //Set up user
    User u1 = [SELECT Id FROM User WHERE Email='[email protected]'];

    test.start();

    //Run As U1
    System.RunAs(u1){

      System.debug('Testing trigger... (single record validation)');

      //NEW Account record
      Account acc = new account();
      acc.Name = 'Test Account';
      acc.Industry = 'Healthcare';

      insert acc;

      //NEW Opportunity record
      Opportunity opp = new Opportunity();
      opp.Name = 'Test Opportunity'; 
      opp.CloseDate = System.today();
      opp.StageName = 'S1 - Investigative';
      opp.Type = 'Repeat Business - Wrkforce HC';
      opp.AccountId = acc.Id;
      opp.Survey_Type__c = 'Product Name';


      insert opp;

      opp.StageName = 'Closed - Lost';
      opp.Reason_Won_Lost__c = 'Relationship';

      update opp;

      //NEW Task record   
      Task testTask = new task();
      testTask.WhatId = acc.Id;
      testTask.Subject = 'Follow-up: ' + opp.name + ' - ' + acc.name;
      testTask.ActivityDate = (opp.CloseDate + 180); 
      testTask.Description = 'Follow up to see how engagement initiatives are going and if we can help. Refer to lost opportunity: ' + opp.name;

      insert testTask;

      //Validate single insert
      System.assertEquals(acc.Id, testTask.WhatId);

    }
    test.stop();

  }
}

That said, I think there are some other issue within the code that you may want to revise. For example, you typically want to setup any data before running your test - which means building those accounts should be done before test.start(); and since you aren't testing the creation of an account it doesn't matter who creates sit - so you don't need to run as u1 for that. There are other best practices to help with structure and creation of test methods you may want to check out as well.

Finally, one glaring thing to me is your first two for loops within your trigger. The first one iterates through all opportunities updated and adds the referenced account to your AccountIds set; then your next for loop iterates through the same list of opportunities but filters them and adds the accounts referenced by those filtered opportunities to the same AccountIds set. This is redundant and probably runs the code against more opportunities than you are expecting. You can remove the initial loop and only store the accounts referenced by the filtered opportunities. It's hard to say what's right there as I'm not sure the requirements.

Welcome to APEX!

2
  • 6
    Those methods only isolate your test execution from a governor limit perspective. That shouldn't have any impact on code coverage (or asserts). Apr 15, 2015 at 15:15
  • Thank you for the warm welcome! This stuff is very cool, if a little frustrating. :)
    – Greg
    Apr 15, 2015 at 22:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .