0

i have written a trigger for making Approval rejection comments mandatory, it works well but i am getting error in test class, posting the trigger and test class here

trigger TR_Reject_Approval_Comments on TR_Currency_Approval__c(before update) 
{

  Map<Id, TR_Currency_Approval__c> rejectedStatements = new Map<Id, TR_Currency_Approval__c>{};
    /* 
      Get the old object record, and check if the approval status 
      field has been rejected  or not
    */
  for(TR_Currency_Approval__c cur: trigger.new)
  {

    TR_Currency_Approval__c oldCur = System.Trigger.oldMap.get(cur.Id);

    if (oldCur.Approval_Status__c != 'Rejected' && cur.Approval_Status__c == 'Rejected')
    { 
      rejectedStatements.put(cur.Id, cur);  
    }
  }
    /* Get the most recent approval process instance for the object.*/
    /* If there are some approvals to be reviewed for approval, then
 get the most recent process instance for each object.*/


  if (!rejectedStatements.isEmpty())  
  {

    List<Id> processInstanceIds = new List<Id>{};

    for (TR_Currency_Approval__c curn : [SELECT 
                                         (SELECT ID FROM ProcessInstances ORDER BY CreatedDate DESC LIMIT 1)
                                      FROM TR_Currency_Approval__c WHERE ID IN :rejectedStatements.keySet()])

    {
        processInstanceIds.add(curn.ProcessInstances[0].Id);
    }

    for (ProcessInstance pi : [SELECT TargetObjectId,
                                   (SELECT Id, StepStatus, Comments 
                                 FROM Steps  ORDER BY CreatedDate DESC
                                 LIMIT 1  )
                               FROM ProcessInstance WHERE Id IN :processInstanceIds ORDER BY CreatedDate DESC]) 

   /* if comments are null in Reject action through the error msg      */  

    {            

     if (pi.Steps[0].Comments == null || 
           pi.Steps[0].Comments.trim().length() == 0)
      {
        rejectedStatements.get(pi.TargetObjectId).addError('Please provide a rejection reason!');
      }
    }  
  }
}

Test class:

@isTest
public class RequireRejectionCommentTest
{

    private static testmethod void testRejectionWithComment()
    {
        TR_Currency_Approval__c cur=new TR_Currency_Approval__c();
        cur.Name='testing1';
        cur.Active__c=true;
        cur.Approval_Description__c='Please Approve';
        cur.Type__c='create';
        insert cur;

        Id curId = generateAndSubmitObject();

        Approval.ProcessWorkitemRequest testRej = new Approval.ProcessWorkitemRequest();
        testRej.setComments('Rejecting request with a comment.');
        testRej.setAction  ('Reject');
        testRej.setWorkitemId(curId);

        Test.startTest();        
                       Approval.ProcessResult testRejResult =  Approval.process(testRej);
        Test.stopTest();

        System.assert(testRejResult.isSuccess(), 'Rejections that include comments should be permitted');
        System.assertEquals('Rejected', testRejResult.getInstanceStatus(), 
          'Rejections that include comments should be successful and instance status should be Rejected');
    }

    private static testmethod void testRejectionWithoutComment()
    {

        TR_Currency_Approval__c cur2=new TR_Currency_Approval__c();
        cur2.Name='testing2';
        cur2.Active__c=true;
        cur2.Approval_Description__c='Please Approve';
        cur2.Type__c='create';
        insert cur2;

        Id cur2Id = generateAndSubmitObject();
        Approval.ProcessWorkitemRequest testRej = new Approval.ProcessWorkitemRequest();
        testRej.setComments('');
        testRej.setAction  ('Reject');      
        testRej.setWorkitemId(cur2Id);

        Test.startTest();        

            try
            {
                Approval.ProcessResult testRejResult =  Approval.process(testRej);
                system.assert(false, 'A rejection with no comment should cause an exception');
            }
            catch(DMLException e)
            {
                system.assertEquals('Please provide a rejection reason!', 
                                    e.getDmlMessage(0), 
                  'error message should be Operation Cancelled: Please provide a rejection reason!'); 
            }
        Test.stopTest();
    }


    private static testmethod void testApprovalWithoutComment()
    {

         TR_Currency_Approval__c cur3=new TR_Currency_Approval__c();
        cur3.Name='testing3';
        cur3.Active__c=true;
        cur3.Approval_Description__c='Please Approve';
        cur3.Type__c='create';
        insert cur3;

        Id cur3Id = generateAndSubmitObject();

        Approval.ProcessWorkitemRequest testApp = new Approval.ProcessWorkitemRequest();
        testApp.setComments ('');
        testApp.setAction   ('Approve');
        testApp.setWorkitemId(cur3Id);

        Test.startTest();        

            Approval.ProcessResult testAppResult =  Approval.process(testApp);
        Test.stopTest();


        System.assert(testAppResult.isSuccess(), 
                     'Approvals that do not include comments should still be permitted');
        System.assertEquals('Approved', testAppResult.getInstanceStatus(), 
           'All approvals should be successful and result in an instance status of Approved');
    }


    private static testmethod void testBatchRejctions()
    {
        List<TR_Currency_Approval__c> testBatchIS = new List<TR_Currency_Approval__c>{};
        for (Integer i = 0; i < 200; i++)
        {
            testBatchIS.add(new TR_Currency_Approval__c());
        }   

        insert testBatchIS;

        List<Approval.ProcessSubmitRequest> testReqs = 
                         new List<Approval.ProcessSubmitRequest>{}; 
        for(TR_Currency_Approval__c testinv : testBatchIS)
        {
            Approval.ProcessSubmitRequest testReq = new Approval.ProcessSubmitRequest();
            testReq.setObjectId(testinv.Id);
            testReqs.add(testReq);
        }

        List<Approval.ProcessResult> reqResults = Approval.process(testReqs);

        for (Approval.ProcessResult reqResult : reqResults)
        {
            System.assert(reqResult.isSuccess(), 
                          'Unable to submit new batch currency Approval record for approval');
        }

        List<Approval.ProcessWorkitemRequest> testAppRejs 
                                                  = new List<Approval.ProcessWorkitemRequest>{};

        for (Integer i = 0; i < 50 ; i++)
        {
            Approval.ProcessWorkitemRequest testRejWithComment = new Approval.ProcessWorkitemRequest();
            testRejWithComment.setComments  ('Rejecting request with a comment.');
            testRejWithComment.setAction    ('Reject');
            testRejWithComment.setWorkitemId(reqResults[i*4].getNewWorkitemIds()[0]);

            testAppRejs.add(testRejWithComment);

            Approval.ProcessWorkitemRequest testRejWithoutComment = new Approval.ProcessWorkitemRequest();
            testRejWithoutComment.setAction    ('Reject');
            testRejWithoutComment.setWorkitemId(reqResults[(i*4)+1].getNewWorkitemIds()[0]);

            testAppRejs.add(testRejWithoutComment);

            Approval.ProcessWorkitemRequest testAppWithComment = new Approval.ProcessWorkitemRequest();
            testAppWithComment.setComments  ('Approving request with a comment.');
            testAppWithComment.setAction    ('Approve');
            testAppWithComment.setWorkitemId(reqResults[(i*4)+2].getNewWorkitemIds()[0]);

            testAppRejs.add(testAppWithComment);

            Approval.ProcessWorkitemRequest testAppWithoutComment = new Approval.ProcessWorkitemRequest();
            testAppWithoutComment.setAction    ('Approve');
            testAppWithoutComment.setWorkitemId(reqResults[(i*4)+3].getNewWorkitemIds()[0]);

            testAppRejs.add(testAppWithoutComment);            
        }

        Test.startTest();        

            try
            {
                List<Approval.ProcessResult> testAppRejResults =  Approval.process(testAppRejs);
                system.assert(false, 'Any rejections without comments should cause an exception');
            }
            catch(DMLException e)
            {
                system.assertEquals(50, e.getNumDml());

                for(Integer i = 0; i < 50 ; i++)
                {
                    system.assertEquals((i*4) + 1, e.getDmlIndex(i));
                    system.assertEquals('Please provide a rejection reason!', 
                                        e.getDmlMessage(i));
                }
            }    
        Test.stopTest();
    }


    private static Id generateAndSubmitObject()
    {

        TR_Currency_Approval__c testIS = new TR_Currency_Approval__c();

        insert testIS;

        Approval.ProcessSubmitRequest testReq = new Approval.ProcessSubmitRequest();
        testReq.setObjectId(testIS.Id);
        Approval.ProcessResult reqResult = Approval.process(testReq);

        System.assert(reqResult.isSuccess(),'Unable to submit new Currency Approval record for approval');

        return reqResult.getNewWorkitemIds()[0];
    }
}

i am getting

System.DmlException: Process failed. First exception on row 0; first error: ALREADY_IN_PROCESS, This record is currently in an approval process. A record can be in only one approval process at a time.: []

These errors and showing code coverage as 33%. Please let me know guys if i am doing any wrong or do i need to add anything.

  • It might help if you indicated which test method(s) are failing. I don't see anything obviously wrong on my first look through the code you've provided – Derek F May 3 '17 at 12:47
  • Actually all the methods are failing, i am getting same error for all the methods – prasad nagamalli May 3 '17 at 13:04
  • Do you have any other triggers on TR_Currency_Approval__c? I still can't see anything that would submit a record to any approval process more than once. Also, I don't see the purpose of creating the TR_Currency_Approval__c records in your first three tests (generateAndSubmitObject() creates a completely new record, and submits that new record to the approval process instead of using the record you generate in the test method itself). – Derek F May 3 '17 at 13:16
  • Actually i am using a process builder to submit the record for approval, is it affects! and dont have any other triggers and processes. – prasad nagamalli May 3 '17 at 13:23
  • 1
    Well, there's your problem then. If you have Process Builder (PB for short) send your record into an Approval Process on insert, then your test records are already in an Approval Process before you submit them to an Approval Process yourself (and as the error you're getting states, a record can only be in 1 Approval Process at a time). I'd suggest removing the bits of your test code that insert records into an Approval Process (i.e. rely on your PB to do that for you), and create another test to ensure that PB is sending new records into an Approval Process. – Derek F May 3 '17 at 16:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.