0

Hello and thank you in advance. I'm having a heck of a time testing my class enough to be able to implement into production. I've created a batchable class that I intend to execute via Execute Anonymous.

The problem I'm seeing is that a) I can't implement the batchable class until 75% of its code is covered b) I've created a test class that uses Database.executeBatch() to run the batch (and provides coverage for the batchable class). However, this is all fine and dandy in a sandbox where I can have the batchable class employed, but in production I receive an error as the BatchMerge class doesn't exists yet (due to not being able to deploy due to lack of code coverage).

Here is my BatchMerge class:

public class BatchMerge implements Database.Batchable<sObject> 
{

    public Database.QueryLocator start(Database.BatchableContext context) 
    {
        return Database.getQueryLocator(
                                            [SELECT 
                                             Contact_to_Merge_Into__c, 
                                             Contact__c, 
                                             Merge_missing_data__c,

                                             // contact data
                                             Contact_to_Merge_Into__r.Phone,
                                             Contact__r.Phone,
                                             Contact_to_Merge_Into__r.Function__c,
                                             Contact__r.Function__c,
                                             Contact_to_Merge_Into__r.Title,
                                             Contact__r.Title,
                                             Contact_to_Merge_Into__r.MobilePhone,
                                             Contact__r.MobilePhone,
                                             Contact_to_Merge_Into__r.How_Contacted__c,
                                             Contact__r.How_Contacted__c,
                                             Contact_to_Merge_Into__r.OtherPhone,
                                             Contact__r.OtherPhone,
                                             Contact_to_Merge_Into__r.LeadSource,
                                             Contact__r.LeadSource,
                                             Contact_to_Merge_Into__r.Fax,
                                             Contact__r.Fax,
                                             Contact_to_Merge_Into__r.Lead_Source_Detail__c,
                                             Contact__r.Lead_Source_Detail__c,
                                             Contact_to_Merge_Into__r.DoNotCall,
                                             Contact__r.DoNotCall,
                                             Contact_to_Merge_Into__r.HasOptedOutOfEmail,
                                             Contact__r.HasOptedOutOfEmail,
                                             Contact_to_Merge_Into__r.Mail_Opt_Out__c,
                                             Contact__r.Mail_Opt_Out__c,
                                             Contact_to_Merge_Into__r.Include_In_Email_Distribution__c,    
                                             Contact__r.Include_In_Email_Distribution__c    
                                             FROM Contact_Merge_Record__c
                                             WHERE Merge_Status__c != 'Merge Completed' ]
                                       );
    }

    public void execute(Database.BatchableContext context, Contact_Merge_Record__c[] records) 
    {        


        for(Contact_Merge_Record__c record: records) 
        {
            String mergedFrom = null;
            record.Last_Merge_Attempt__c = system.now();
            update record;


            /// Update Detail Vars


            try 
            {   
                // forces contact field changes to be tracked.
                mergedFrom = record.Contact__c;
                record.Contact__c = record.Contact__c;
                update record;

                // merge/delete
                Database.merge(new Contact(Id=record.Contact_to_Merge_Into__c), record.Contact__c); 

                // update extra data based on merge missing data flag
                if(record.Merge_missing_data__c == true)//record.Merge_missing_data__c == true)
                {     

                     // Capture updates to be made

                     List<Contact_Merge_Record_Update__c> RecordUpdates = new List<Contact_Merge_Record_Update__c>();
                     String Merged_Contact_ID = record.Contact__r.ID; // doesn't change
                     ID Contact_Merge_Record = record.ID; // doesn't change

                     String Updated_field = null;
                     String Master_Record_original_value = null;             
                     String Updated_Value = null;
                     String Merge_Contact_original_value = null;


                    update record.Contact_to_Merge_Into__r; // wait to make updates until update detail records are made (validate?)
                    insert RecordUpdates;// insert updates

                               // update record.Contact_to_Merge_Into__r;   // from above
                } // end if


                //update record to point to master record;

                record.Contact__c = record.Contact_to_Merge_Into__c;      
                record.Merge_Status__c = 'Merge Completed';
                record.Merge_Date__c = system.now();
                record.Contact_Merged__c = mergedFrom;
                record.Merge_Details__c = null;
                update record; 
            } // end try




                                catch(Exception e) 
                                {
                                    if(record.Contact_to_Merge_Into__c == mergedFrom)
                                    {
                                        record.Merge_Details__c = 'Cannot merge the same record into the same record.';
                                    }

                                    else
                                    {
                                        record.Merge_Details__c = e.getMessage() + ', due to: ' + e.getCause();
                                    }

                                    record.Merge_Status__c = 'Merge failed (See Details)';
                                    update record;
                                }
        } // end loop

    }




    public void finish(Database.BatchableContext context) 
    {


    }
}

Here is my test class:

@IsTest(SeeAllData=true)
public with sharing class BatchMergeTestClass 
{



    @IsTest(SeeAllData=true)
    static void testUpdate()
    {

        Database.executeBatch(new BatchMerge(), 10);


    }
}

Here is my UPDATED test class:

@IsTest
public with sharing class BatchMergeTestClass 
{

    @IsTest
    static void testUpdate()
    {

        List<Account> Accounts = new List<Account>();                  
        List<Contact> Contacts = new List<Contact>();
        List<Contact_Merge_Record__c> ContactMergeRecords = new List<Contact_Merge_Record__c>();

        Accounts.add(new Account(Name = 'Test Account'));
        Contacts.add(new Contact(FirstName = 'Test First 1', LastName = 'Test Last 1'));
        Contacts.add(new Contact(FirstName = 'Test First 2', LastName = 'Test Last 2'));

        insert Accounts;
        insert Contacts;

        Contact Contact1 = new Contact(FirstName = 'Test First 1', LastName = 'Test Last 1');
        Contact Contact2 = new Contact(FirstName = 'Test First 2', LastName = 'Test Last 2');

        Contacts.add(Contact1);
        Contacts.add(Contact2);        
        insert Contacts; 

        Contact_Merge_Record__c ContactMergeRecord = new Contact_Merge_Record__c();

        ContactMergeRecord.Contact_to_Merge_Into__c = Contact1.Id;
        ContactMergeRecord.Contact__c = Contact2.Id;        
        ContactMergeRecords.add(ContactMergeRecord);        
        insert ContactMergeRecords;

        BatchMerge bm = new BatchMerge();      
        Test.startTest();        
        Database.executeBatch(bm, 10);         
        ID batchprocessid = Database.executeBatch(bm);
        Test.stopTest();

    }
}
15
  • Are you deploying the batch class and its associated test cases at the same time to production? Commented Dec 9, 2015 at 1:30
  • @DanielBallinger I'm trying to deploy the batch class, but failing due to only 45% test coverage. As far as test cases, are you referring to production data? Maybe it's failing due to not having data in production that penetrates the if statements?
    – tchock
    Commented Dec 9, 2015 at 1:38
  • @parrotsnest - Are you creating your test data in the test class? Maybe post your test class. Also, what is your prod coverage before you attempt to deploy?
    – Eric
    Commented Dec 9, 2015 at 1:45
  • 1
    I'm referring to your test class the calls Database.executeBatch(). This class should be creating all the data you need to test the corresponding batch class. Be sure you aren't using @IsTest(SeeAllData=true). Commented Dec 9, 2015 at 1:47
  • 1
    You need to deploy both the batch class and test class at the same time to production. There are several ways to do this. One option is a changeset from a sandbox with both classes included. Another is using a Metadata API package that includes both. How are you doing the deployment to production? Commented Dec 9, 2015 at 1:55

1 Answer 1

1

To recap the discussion from the comments:

  1. Do not use @IsTest(SeeAllData=true).
    With this on your test case is accessing the existing data in the current org. This makes for a very fragile test case as the data that the test depends on can vary over time and between orgs. The syntax exists for a few exceptional scenarios that can't currently be tested with fully scripted data. Avoid it unless you absolutely need to use it.
  2. Because you won't be using SeeAllData=true, your test case will need to script in any data that is required to exercise the batch code.
  3. To deploy the batch process to production you also need to deploy the associated test class at the same time to give the required coverage. For a typical Sandbox to Production deployment this means putting both into the same changeset.
  4. A batch processes is an asynchronous one. As such, test cases that start a batch should surround the Database.executeBatch() call with Test.startTest(); and Test.stopTest();. This will force the batch to run an iteration synchronously within the test method.

You must log in to answer this question.

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