+ Start a Discussion
merrick devillemerrick deville 

Trail head using future Mehods

Hello everyone I could use some help on the using future Methods trailhead. Please keep in mind I am not an Apex coder at all but I am trying to learn as much as I can.

Here is the Task

Create an Apex class with a method using the @future annotation that accepts a List of Account IDs and updates a custom field on the Account object with the number of contacts associated to the Account. Write unit tests that achieve 100% code coverage for the class.
Create a field on the Account object called 'Number_of_Contacts__c' of type Number. This field will hold the total number of Contacts for the Account.
Create an Apex class called 'AccountProcessor' that contains a 'countContacts' method that accepts a List of Account IDs. This method must use the @future annotation.
For each Account ID passed to the method, count the number of Contact records associated to it and update the 'Number_of_Contacts__c' field with this value.
Create an Apex test class called 'AccountProcessorTest'.
The unit tests must cover all lines of code included in the AccountProcessor class, resulting in 100% code coverage.
Run your test class at least once (via 'Run All' tests the Developer Console) before attempting to verify this challenge.

I have written an Apex class and a Test class but I think I am doing them both Wrong: because when I try to check the challenge i Get this error

Challenge Not yet complete... here's what's wrong: 
The 'AccountProcessorTest' test class doesn't appear to be calling the 'AccountProcessor.countContacts' method between Test.startTest() and Test.stopTest().


When I run the Test class I get this error :

System.QueryException: List has no rows for assignment to SObject

Here is the CLASS:
 
public class AccountProcessor {
     @future

  public static void someFutureMethod(List<id> scope) {

   Account[] updates = new Account[] {};
        for (AggregateResult ar : [
                select AccountId a, count(Id) c
                from Contact
                where AccountId in :scope
                group by AccountId
                ]) {
            updates.add(new Account(
                    Id = (Id) ar.get('a'),
                    Number_of_Contacts__c = (Decimal) ar.get('c')
                    ));
        }
        update updates;
    }

}

Here is the Test Class:
 
@IsTest
public class AccountProcessorTest {
  public static testmethod void TestAccountProcessorTest() {
 
Test.startTest();
     Account a = new Account();
        a.Name = 'Test Account';
        Insert a;
      
      Contact cont = New Contact();
      
      cont.FirstName ='Bob';
      cont.LastName ='Masters';
      cont.AccountId = a.Id;
      Insert cont;

    Test.stopTest() ;
     Contact ACC = [select AccountId from Contact where id = :a.id LIMIT 1];

 
        System.assert(Cont.AccountId != null);
        System.assertequals(cont.id, ACC.AccountId);

  }}

I have used alot or diffrent google searches to get me this far. But I have not Idea if Im even remotly close to the right answer or not.


 
Best Answer chosen by merrick deville
Amit Chaudhary 8Amit Chaudhary 8
As per task you method should be "countContacts"

Please update your Apex class like below :-
public class AccountProcessor 
{
  @future
  public static void countContacts(Set<id> setId) 
  {
      List<Account> lstAccount = [select id,Number_of_Contacts__c , (select id from contacts ) from account where id in :setId ];
      for( Account acc : lstAccount )
      {
          List<Contact> lstCont = acc.contacts ;
          
          acc.Number_of_Contacts__c = lstCont.size();
      }
      update lstAccount;
  }
}

Test class:-
@IsTest
public class AccountProcessorTest {
    public static testmethod void TestAccountProcessorTest() 
    {
        Account a = new Account();
        a.Name = 'Test Account';
        Insert a;

        Contact cont = New Contact();
        cont.FirstName ='Bob';
        cont.LastName ='Masters';
        cont.AccountId = a.Id;
        Insert cont;
        
        set<Id> setAccId = new Set<ID>();
        setAccId.add(a.id);
 
        Test.startTest();
            AccountProcessor.countContacts(setAccId);
        Test.stopTest();
        
        Account ACC = [select Number_of_Contacts__c from Account where id = :a.id LIMIT 1];
        System.assertEquals ( Integer.valueOf(ACC.Number_of_Contacts__c) ,1);
  }
  
}

Please let us know if this will help you

Thanks
Amit Chaudhary
 

All Answers

Deepak GulianDeepak Gulian
There is no method named "countContacts" in your apex class.
Amit Chaudhary 8Amit Chaudhary 8
As per task you method should be "countContacts"

Please update your Apex class like below :-
public class AccountProcessor 
{
  @future
  public static void countContacts(Set<id> setId) 
  {
      List<Account> lstAccount = [select id,Number_of_Contacts__c , (select id from contacts ) from account where id in :setId ];
      for( Account acc : lstAccount )
      {
          List<Contact> lstCont = acc.contacts ;
          
          acc.Number_of_Contacts__c = lstCont.size();
      }
      update lstAccount;
  }
}

Test class:-
@IsTest
public class AccountProcessorTest {
    public static testmethod void TestAccountProcessorTest() 
    {
        Account a = new Account();
        a.Name = 'Test Account';
        Insert a;

        Contact cont = New Contact();
        cont.FirstName ='Bob';
        cont.LastName ='Masters';
        cont.AccountId = a.Id;
        Insert cont;
        
        set<Id> setAccId = new Set<ID>();
        setAccId.add(a.id);
 
        Test.startTest();
            AccountProcessor.countContacts(setAccId);
        Test.stopTest();
        
        Account ACC = [select Number_of_Contacts__c from Account where id = :a.id LIMIT 1];
        System.assertEquals ( Integer.valueOf(ACC.Number_of_Contacts__c) ,1);
  }
  
}

Please let us know if this will help you

Thanks
Amit Chaudhary
 
This was selected as the best answer
Amit Chaudhary 8Amit Chaudhary 8
If your issue is resolved then please close this thread after marking best answer do that is if some has issue that post can help other
Bhanu ErraguntlaBhanu Erraguntla
Thanks Amit.  This helped.  I have used a list instead of set and that worked!

-Bhanu
merrick devillemerrick deville
Amit,

Thanks for the help!
Swathi MiryalaSwathi Miryala
Thank you Amit for the help
Naveen ChoudharyNaveen Choudhary
Thanks Amit!!!
 
Fnu SumitFnu Sumit
Amit i use your code and befor check to challange i use run all. but it wont cover 100% code.
SHISHIR BANSALSHISHIR BANSAL
Methods defined as TestMethod do not support Web service callouts  


this error is coming while using your code 
Harsh GosarHarsh Gosar
If you get this error : Methods defined as TestMethod do not support Web service callouts
Try testing  AccountProcessor -> class and test class in new org with only @future or @future(callout = false) annotation 
Amidou CisseAmidou Cisse
Thank You Amit !
Carlos FrancoCarlos Franco

Hello Amit... I would like to know how I can set a specific ID in AccountProcessorTest... I tried

@IsTest
public class AccountProcessorTest {
    public static testmethod void TestAccountProcessorTest() {
        Test.startTest();
            AccountProcessor.countContacts('0015000001I2JpXAAV');
        Test.stopTest();
  }
}

And got this error --> Method does not exist or incorrect signature: AccountProcessor.countContacts(String)

 

Thanks in advance

Sindhura ManthapuriSindhura Manthapuri
@carlos Franco: This is because "AccountProcessor" apex class is not saved. Please save "AccountProcessor" class and this will remove the error.
Neeraj Kumar 108Neeraj Kumar 108
I am getting Error: System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Number_Of_Contacts__c]: [Number_Of_Contacts__c]
Cezary ZawadzkiCezary Zawadzki
@Neeraj Kumar 108 when creating field Number_Of_Contacts_cc you probably marked it as required and when creating Account for test you don't provide value for this field.
ChubbyChubby
I am getting Variable does not exist:Contacts error. Can someone please help?
Ben RowleyBen Rowley
Hi, 
Please try this:

Create this class:
public class AccountProcessor {
   
    @Future
    public static void countContacts(List<Id> accountIds){
        Map<Id,List<Contact>> accContacts = new Map<Id,List<Contact>>();
        List<Account> accsForUpdate = new List<Account>();
        if(accountIds != null){
            For(Account acc : [SELECT id,(SELECT id,Name FROM Contacts)FROM Account where id in: accountIds]){
                  accContacts.put(acc.Id,acc.contacts);
             }
            
            for(Id key : accContacts.keySet()){
                Account a = New Account(id = key);
                a.Number_of_Contacts__c = accContacts.get(key).size();
                accsForUpdate.add(a);
            }
        update accsForUpdate;
        }
    }
}
Now create this Test class. 
 
@isTest
public class AccountProcessorTest {
 
    @testSetup 
    static void setupAccount(){

    List<Account> accounts = RandomAccountContactFactory.generateRandomAccounts(1);
    insert accounts;
    
    
    List<Contact> contacts = RandomAccountContactFactory.generateRandomContacts(3, 'TestAP' , accounts.get(0).id);
    insert contacts;
  	
    }
    
	 @isTest
     static void testAccountProcessor(){
     
     List<id> accIds = new List<id>();
		for(Account a : [select id from Account]){
            accIds.add(a.id);
        }
         Test.startTest();
         AccountProcessor.countContacts(accIds);
         Test.stopTest();
     
     }
}

And this class to be called from the test class to create test Account with 3 Contacts (to be counted). 
public class RandomAccountContactFactory {
    
    public static List<Contact> generateRandomContacts (Integer numContacts, String lastName,Id accId){
        List<Contact> contacts = new List<Contact>();
        
        for(integer i = 0; i<numContacts; i++){
            Contact c = new Contact();
            c.FirstName = 'Trail' + i;
            c.LastName = lastName + i;
            c.AccountId = accId;	
			contacts.add(c);
        }
        return contacts;
    }
    

    public static List<Account> generateRandomAccounts (Integer numAccounts){
        List<Account> accounts = new List<Account>();

        for(integer i = 0; i<numAccounts; i++){
            Account a = new Account();
            a.Name = 'Test' + i;
            accounts.add(a);
        }
        return accounts;
    }
    
}


    
    
KapavariVenkatramanaKapavariVenkatramana
@ Thank you Amit for the help
Jagdeep MankotiaJagdeep Mankotia
Must create RandomAccountContactFactory class. Thanks Ben your code is working fine for me.