1

I want to deploy a package that has an apex class, test class and a process builder that calls the apex class. On the sandbox where I developed the class, the code coverage is 100%. When I tried to deploy it to another sandbox/production, it failed because it showed the code coverage to be 65%.

I believe the issue is because the process builder is inactive when it is deployed and the entire code is not covered as a result. How should I proceed with this?

I have already tried to do the following:

  • Deploy the process builder first to activate it before deploying the classes. - Deploying the process builder failed.
  • Deploy the classes without the process builder; code coverage was 65%.
  • Change the test class to accommodate more cases. This was not possible as I changed the code to work with process builder and I cannot find a way to test it.
  • I ran the test code when the process builder was activated and deactivated. It showed 65% coverage when it was deactivated, and 100% coverage when it was activated, as the process builder is called when a record is inserted in the test class.

My code takes the customer email and converts it to a hash using CryptoUtil.generateHashDigest method and then saves it in the hashed email field.

Public static void newRecord(List<Account> listAccounts) {
    for(Account a : listAccounts) {
        Account updacc=[select id from account where id = :a.id];
        String message = String.valueof(a.get('Customer_Email__pc'));
        String hashDigest = CryptoUtil.generateHashDigest(message);
        updacc.Hashed_email__pc = HashDigest;
        update updacc;
    }
}

I had to create a clone of the account record inserted/updated in order to use process builder. Using this method, the changes are only made in the clone. If process builder is not used, the test class gets a Null value instead of the actual hash value in the Hashed_email__pc field which leads to the test failing. When process builder is used, the changes made in the clone are reflected in the actual record, and the test passes. Even if I do not have a test method calling this section of code, the test passes as the process builder covers it.

I cannot figure out a way of creating a test class where the correct values are returned when the process builder is deactivated. I have to use DML to insert the record so that it can be cloned.

My test class is as follows:

@isTest static void testInsertingNewRecordEmailNotNull() {
    List<Account> acct = new List<Account>();
    acct.add(new Account(LastName= 'Smith', Customer_Email__pc='abc123@gmail.com'));
    insert acct;                                                           
    CryptoUtil.newRecord(acct);
    System.assertEquals('ZSev/yqjeUZX0vLKGhRot2XibFC3gE4qw3VtZHZhFHQ=', acct[0].Hashed_email__pc);
}

How should I test the apex class in this case?

  • What about deploying classes together with process builder? – kurunve Mar 6 '17 at 17:01
  • Ideally your test class should cover your apex class regardless of whether or not the process runs. What problems are you having testing the code without the process active? – John Towers Mar 6 '17 at 17:03
  • In general way, I can suggest you to write your logic classes, splitted by modules. In that case, test classes can cover your logic without actual DML, so you can achiever coverage regardless workflow/flow/process builder/required fields/validation rules etc. – kurunve Mar 6 '17 at 17:04
  • I have added my code and explained why it is not possible to create a test class without DML. Is there any workaround that can be done? Thanks! – Aish Mar 7 '17 at 10:12
2

You should write your invocable Apex so that you can test it without the flow invoking it at all. Whether it actually gets invoked is a configuration detail and should be verified using functional testing, not automated unit tests.

The basic structure should be:

Invocable Logic

public with sharing class MyInvocableClass
{
    @InvocableMethod
    public static void doStuff(List<MyObject__c> records)
    {
        // logic
    }
}

Test

@IsTest
class MyInvocableClassTests
{
    static testMethod void testDoStuff()
    {
        List<MyObject__c> records = new List<MyObject__c>
        {
            new MyObject__c(/*data*/)
        };
        // no need to insert unless your invocable method re-queries

        Test.startTest();
            MyInvocableClass.doStuff(records);
        Test.stopTest();

        // assert on effects of invocable method
    }
}

Note that the way you should test the method is calling the invocable method directly, not performing DML on your records and hoping the flow invokes the method. It's the same idea as a Service Layer architecture, where you should test the trigger and the service separately. Only in this case, the trigger is declarative, so you shouldn't even use automated testing for that.

|improve this answer|||||
  • Thanks for the answer! I have already tried writing the test method that follows structure that you mentioned in your answer. I have added my code and explained why it is not possible to create a test class without DML in this particular case. Is there any workaround that can be done? – Aish Mar 7 '17 at 10:15

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.