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
José Teixeira GomesJosé Teixeira Gomes 

Need help with a test class for this trigger

Trigger FollowUpTask on Task(after update) {

     Set<Id> completedTasks = new Set<Id>();
     Set<Id> contactIds = new Set<Id>();
     Set<Id> accountIds = new Set<Id>();
 
     for (Task task: Trigger.new) {
     
          if (task.Status != Trigger.oldMap.get (task.Id).Status && task.Status=='Completed')
               completedTasks.add(task.Id);
               contactIds.add(task.WhoId);
               accountIds.add(task.WhatId);
          }
 
     contactIds.remove(null);
     accountIds.remove(null);
 
     Map<Id, Contact> contactMap = new Map<Id, Contact>();
     Map<Id, Account> accountMap = new Map<Id, Account>();
 
     if (!contactIds.isEmpty()) {
          contactMap = new Map<Id, Contact>([
               select Contact_Frequency__c,
                    Name
               from Contact
               where Id in :contactIds
          ]);
     }
 
     if (!accountIds.isEmpty()) {
          accountMap = new Map<Id, Account>([
               select Primary_Contact__c,
                    Name,
                    Tier__c
               from Account
               where Id in :accountIds
          ]);
     }

     List<Task> newTasks = new List<Task>();
     for (Id id: completedTasks) {
          Task task = Trigger.newMap.get(id);
          Contact contact = contactMap.get(task.WhoId);
          Account account = accountMap.get(task.WhatId);
 
          if (account == null || contact == null) {
               continue;
          }
 
          Task newTask = new Task(
               Subject = 'Follow Up ' + account.Name + '/' + contact.Name,
               OwnerId = account.OwnerId,
               WhoId = account.Primary_Contact__c,
               WhatId = account.Id,
               Priority = (account.Tier__c == 'Tier 1') ? 'High' : 'Normal'
               );    
 
          Date duedate = system.today().addDays((Integer)(contact.Contact_Frequency__c));
          newTask.ActivityDate = dueDate;
 
          newTasks.add(newTask);
     }
 
     if (!newTasks.isEmpty()) {
          insert newTasks;
     }
}

Best Answer chosen by José Teixeira Gomes
pconpcon
You will (and should) create a new account, contact and task for each test.  Then you'll use SOQL to fetch the tasks back

static testMethod void myTest() {
     Account testAccount = new Account(...);
     insert testAccount;

     Contact testContact = new Contact(...);
     insert testContact;

     Task testTask = new Task(...);
     insert testTask;

     testTask.Status = 'Completed';

     Test.startTest();

     update testTask;

     Test.stopTest();

     List<Task> results = [
          select Subject,
               OwnerId,
               WhoId,
               WhatId,
               Priority
          from Task
          where WhatId = :testAccount.Id and
               WhoId = :testContact.Id and
               Status != 'Completed'
     ];

     System.assertEquals(1, results.size(), 'Did not get the expected number of tasks');
     System.assertEquals('...', results.get(0).Subect, 'did not get the right subject back');
     ...
} 

NOTE: This code has not been tested and may contain typographical or logical errors

All Answers

pconpcon
Have you written any tests for it at all?
  • I would suggest you start with a simple postive test that updates a task to completed and then fetches the tasks for that account/contact and checks to see that your new task was inserted.
  • Then create a negative test that updates a task, but doesn't mark it complete and make sure no new tasks are completed.
  • Then create a bulk update test that has a mix of newly completed task as well as tasks that are just being updated.
For an example of this type of testing, see this post [1].  It is directled at leads but should give you enough information to get started on for your tasks

[1] http://blog.deadlypenguin.com/blog/2014/07/23/intro-to-apex-auto-converting-leads-in-a-trigger/
José Teixeira GomesJosé Teixeira Gomes
Yes I have written a test but I think it is not exactly well specified because I do not think I am testing for a bulk operation. My problem is (I think) that I need to create the accounts, contacts and tasks with all of the utilized fields in the trigger as well. The test only got 73% of the lines covered, with the missing lines being the mapping and the end of the trigger. How do I check if a new task was created for a given account?
pconpcon
You will (and should) create a new account, contact and task for each test.  Then you'll use SOQL to fetch the tasks back

static testMethod void myTest() {
     Account testAccount = new Account(...);
     insert testAccount;

     Contact testContact = new Contact(...);
     insert testContact;

     Task testTask = new Task(...);
     insert testTask;

     testTask.Status = 'Completed';

     Test.startTest();

     update testTask;

     Test.stopTest();

     List<Task> results = [
          select Subject,
               OwnerId,
               WhoId,
               WhatId,
               Priority
          from Task
          where WhatId = :testAccount.Id and
               WhoId = :testContact.Id and
               Status != 'Completed'
     ];

     System.assertEquals(1, results.size(), 'Did not get the expected number of tasks');
     System.assertEquals('...', results.get(0).Subect, 'did not get the right subject back');
     ...
} 

NOTE: This code has not been tested and may contain typographical or logical errors
This was selected as the best answer
pconpcon
Also, want to add ActivityDate to that query and assert that it is set correctly based on your creation of the account/contact record
José Teixeira GomesJosé Teixeira Gomes
Wow. I had something very similar already but I had a mistake when it came to the List. This is what I have:

@istest
public class FollowUpTaskTest {

  static testmethod void testFollowUpTask ()
  {
      Contact Ctc = new Contact (LastName = 'TestContact',
                                 Contact_Frequency__C = 30);
      
      Account Acc = new Account (Name = 'TestAccount',
                                 Rank__C = 3,
                                 Primary_Contact__C = Ctc.Id,
                                 OwnerId = '00520000003j1B2');
      Ctc.AccountId = Acc.Id;
      
      Date Today = System.today();
          
      Task Tsk = new Task (	Subject = 'TestTask', 
                   			OwnerId = '00520000003j1B2',
                   			WhoId = Ctc.Id,
                   			WhatId = Acc.Id,
                   			Priority = 'Normal',
                           	Status = 'Not Started',
                           	ActivityDate = Today
                           	);
       insert Acc;
       insert Ctc;
       insert Tsk;
          
  		Test.startTest();
        Tsk.Status = 'Completed';
        Update Tsk;
      	Test.stopTest();
        
      List<Task> results = [select Id, Subject, OwnerId, WhatId, WhoId, Priority, Status, ActivityDate
                 			from Task
                 			where WhatId = :Acc.Id
                 			and WhoId = :Ctc.Id
                    		and Status != 'Completed'];
But I am not sure how to do the assertion part with lists. Also, don't I need to create like 50 accounts, contacts, etc.?

pconpcon
For the single test, you'll just make sure you only got a single task back (line 31 of my example) and then you'll compare what you expect to get back for each field.  So on line 32 I have results.get(0).Subject and that will look at the first result and compare it's subject.  If you wanted to, you could just say

Task result = results.get(0);

and use result for all of your asserts.
José Teixeira GomesJosé Teixeira Gomes
Thanks. I did that but the test is failing and I get the error: List has no rows for assignment to SObject on line 33. Which seems odd because that would mean that the trigger is not creating anything right?
José Teixeira GomesJosé Teixeira Gomes
The error now is List index out of bounds on the Task result = ...
pconpcon
Yes, but this is because your test code is written incorrectly.  The order of the inserts (and creations of the objects) matter.  In your code,  you create the Task object before you insert the account and the contact.  So on lines 19 and 20 the Ids are null since they have not be inserted.  You still should not get that error (assuming you did a List<Task> instead of just Task) because it would just make the List empty.

If you move your inserts up (I suggest directly under your creation of the object like in my example) it should work.
José Teixeira GomesJosé Teixeira Gomes
Did as you suggested but actually I had something wrong in the code of the trigger. Now I get coverage of 96%! Just the continue line is not being tested but that is because of that negative test that is not being done right?
Again, thank you very much for your help. One last thing, despite the code coverage I still get the x of failed in the developer console. Why is that?
pconpcon
That is because a case of the negative test isn't being covered.  You need to create a task with a contact but with no account.  That will cover your continue line.  As for the X on the console for the test, it is a known issue [1]

[1] https://success.salesforce.com/issues_view?id=a1p30000000T0FeAAK
José Teixeira GomesJosé Teixeira Gomes
Awesome. Thanks pcon