3

I'm trying to unit test the class below, which does against the ProcessInstanceWorkitem table to find all pending approvals for an sobject (given by name in the method call). The class is rather generic, designed to work against any sobject and independently of any specifically defined Approval Process. Its supposed to be packaged as part of a Lightning Component.

I'm struggling with properly unit testing this class since I do not have or want to define an approval process in the Setup menu (since then I'd have to package that one as well).

Is there any way to properly test a class like this without having an approval process be defined in the Org?

I've tried inserting a ProcessInstanceWorkItem manually in a test class, but this doesn't work since I'd need to insert a ProcessDefinition first (where I get a DML not supported exception).

public class MyApprovalsCtrl {
    @AuraEnabled
    public static List<sobject> getPendingApprovals(String sobjectName, String[] fields) {
        Id userId = UserInfo.getUserId();
        List<ProcessInstanceWorkitem> wis = [
            SELECT Id, ProcessInstance.TargetObjectId 
            FROM   ProcessInstanceWorkitem
            WHERE  ProcessInstance.Status = 'Pending'
            AND    ProcessInstance.TargetObject.Type = :sobjectName
            AND    ActorId = :userId
        ];

        if(wis.size() > 0) {
            String idList = '';
            for(ProcessInstanceWorkitem wi: wis) {
                idList += '\'' + wi.ProcessInstance.TargetObjectId + '\'' + ',';
            }
            idList = idList.removeEnd(',');

            String fieldList = '';
            for(String field: fields) {
                if(field != null && !String.isBlank(field))
                fieldList += ',' + field;
            }            

            String query = 'SELECT Id'+ fieldList + ' FROM ' + sobjectName + ' WHERE Id IN (' + idList + ')';
            List<sobject> objectsWithPendingApproval = Database.query(query);

            return objectsWithPendingApproval;
        }
        else {  
            return new List<sobject>();
        }
    }
}

1 Answer 1

1

You can't test the insertion of ProcessInstanceWorkItem records without having an Approval Process defined in your org. You absolutely must set one up.

Take a look at Apex Approval Processing Example, the basic idea in the code is that you have to actually submit a record for approval.

Approval.ProcessSubmitRequest req1 = new Approval.ProcessSubmitRequest();
// set stuff
Approval.ProcessResult result = Approval.process(req1);

There is no other way to create these records. As you have found, direct DML Operations on these objects are not supported.


You can, however, use Separation Of Concerns to your advantage to create more testable code. Here is a stub of the methods you need:

@AuraEnabled
public static List<SObject> getPendingApprovals
    (String objectName, List<String> fields)
{
    return getPendingApprovals(getTargetObjectIds([/*query*/], objectName, fields);
}
public static Set<Id> getTargetObjectIds
    (List<ProcessInstanceWorkItem> workItems)
{
    // implementation
}
public static List<SObject> getPendingApprovals
    (Set<Id> ids, String objectName, List<String> fields)
{
}

Now the vast majority of your code is easy to test, and you just have one pesky line that is more challenging. Note that your workItems do not need to be inserted to pass them to the getTargetObjectIds method, and you don't need to manipulate ProcessInstanceWorkItem records at all to test the more independent version of getPendingApprovals.

3
  • 1
    My end goal is to package that code (together/as part of a lightning component). I can define an approval process and have the unit tests run in my developer org. But I don't have any control over if and what approval processes run in the target org where the package would be deployed... so basically I'm left with either having failing unit tests in all target orgs or cheating my way to enough test coverage without having an approval process defined?
    – Miichi
    Commented Nov 15, 2016 at 14:49
  • 1
    @Miichi Take a look at the skeleton I added to my post. Use Separation Of Concerns to your advantage here.
    – Adrian Larson
    Commented Nov 15, 2016 at 14:58
  • @Miichi - You do not have to package the approval process just FYI. And in your tests, check for the existence of an approval process when running and if not there exit the test. This will prevent your tests failing in the customer org but reduce coverage (not a concern in customer org). You will want to try and get as much testing working though and follow what Adrian said. You should be able to break it out to just a few lines not covered in customer org.
    – Eric
    Commented Nov 15, 2016 at 16:18

You must log in to answer this question.

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