function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Søren Nødskov HansenSøren Nødskov Hansen 

How to create AgentWork in Unit Test?

Hi all

I have an after update Trigger which runs off AgentWork. If the WorkItemId of the AgentWork record is a Case and field AcceptDateTime changes, then the Trigger updates a field on the related Case.

Now, all of that works like a charm. However, writing a Unit Test Class for this has proven slightly tricky and I'm hoping someone in here can help me with that.

I keep getting this error: "System.DmlException: Insert failed. First exception on row 0; first error: FIELD_INTEGRITY_EXCEPTION, The agent's status is not associated with the channel for this work.: [ServiceChannelId]"

The User inserting the AgentWork record in the Unit Test Class has a Profile which is coupled with our ServiceChannel and the AgentWork record I'm trying to insert has ServiceChannelId, WorkItemId (Id of Parent Case) and UserId (of User with correct Profile) set.

What am I missing?

Thanks in advance.

Søren Nødskov Hansen
Something Cloud
Best Answer chosen by Søren Nødskov Hansen
Jelle HendriksJelle Hendriks
So I assume you have setup OmniChannel? Your ServiceChannel is called Email which supports Cases? I guess you also have a routing configuration in place which routes cases from a specific queue to OmniChannel? If so, have you tried to make yourself member of the queue, set yourself to available through the Salesforce console, and than ran the tests? (Don't forget to insert the AgentWork with 
System.runAs()) 

All Answers

Søren Nødskov HansenSøren Nødskov Hansen
I sould mention that I have also tried inserting both ServiceChannelStatus and PresenceUserConfigProfile in my Unit Test but in both cases I get a message saying that "DML not allowed on ServiceChannelStatus/PresenceUserConfigProfile".

Søren Nødskov Hansen
Something Cloud
Jelle Hendriks 6Jelle Hendriks 6
I'm encountering the same issue. No answer yet.. 
kdcinkdcin
I am also having this problem. I have a trigger that sets the related case status to working when the agent accepts the case (agentwork record status is updated to opened), but I am unable to write the unit test and therefore unable to deploy to production.
Jelle HendriksJelle Hendriks
We had the same problem. When you run your testclass. Make sure you are a member of the relevant ServiceChannel. Set yourself in Omni-Channel to Available. Than run the testclass/deployment. You'll see it succeed. This is our code... 

trigger TaskCreatesWorkItem on Task (after insert, after update) {
   
ServiceChannel serviceChannel = [SELECT Id FROM ServiceChannel WHERE Masterlabel = 'PushCases'];
List<AgentWork> lAgentWorkToInsert = new List<AgentWork>();

    for(Task t : trigger.new) {

            if(t.Subject.contains('call')){
                AgentWork aw = new AgentWork();
                aw.WorkItemId = t.WhatId;
                aw.ServiceChannelId = serviceChannel.id;
                aw.UserId = t.OwnerId;

                lAgentWorkToInsert.add(aw);
        }
    }
    try{
        Database.SaveResult[] sr =  Database.insert(lAgentWorkToInsert, false);    
    } catch(Exception e){

    }


TESTCLASS:

@isTest
private class Test_TaskCreatesWorkItem {
    
    @isTest static void test_method_one() {

ServiceChannel serviceChannel = [SELECT Id FROM ServiceChannel WHERE Masterlabel = 'PushCases'];

        User user1 = new User();
        user1.id = UserInfo.getUserId();
        
        Account acnt1 = new Account(FirstName = 'John',
                                LastName = 'Doe',
                                personEmail = 'jdoe_test_test@doe.com');
        insert acnt1;

        Case case1 = new Case(Origin = 'Facebook', 
                                Accountid = acnt1.id);

        insert case1;

        System.runAs(user1){    
        Task task1 = new Task(Subject = 'call',
                                WhatId = case1.id);
        insert task1;
        

            }


    }
    
}
Søren Nødskov HansenSøren Nødskov Hansen
Hi Jelle

Thanks for replying.

I think my issue is somewhat different from yours. Your trigger and unit test work on Task where mine works on AgentWork. I.e. my trigger executes on the after context of the AgentWork object and therefore my unit test starts out by inserting an AgentWork record and then later updating it.

This is my Apex Class:
public static void setCaseDatetimeAccepted(Map<Id, AgentWork> newAgentWorks, Map<Id, AgentWork> oldAgentWorks) {
        Map<Id, Case> casesToUpdate = new Map<Id, Case>();
        AgentWork oldAgentWork = null;
        String workItemId = null;
        for (AgentWork newAgentWork : newAgentWorks.values()) {
            oldAgentWork = oldAgentWorks.get(newAgentWork.Id);
            workItemId = String.valueOf(agentWork.WorkItemId);
            if (newAgentWork.AcceptDateTime != oldAgentWork.AcceptDateTime &&
                	workItemId != null &&
                	workItemId.substring(0, 3) == Case.sObjectType.getDescribe().getKeyPrefix()) {
                casesToUpdate.put(workItemId, new Case(Id = workItemId,
                                                       Date_Time_Accepted__c = newAgentWork.AcceptDateTime));
            }
        }
        if (casesToUpdate.size() > 0) {
            update casesToUpdate.values();
        }
    }
This is my unit test:
static testMethod void setCaseDatetimeAcceptedTest() {
        // Create Test Data
        upsert TestDataGenerator.createGlobalSettings();
        User csUser = TestDataGenerator.createTestUsers(1, null).get(0);
        insert csUser;
        Datetime datetimeAccepted = System.now();
        List<Case> testCases = TestDataGenerator.createCases(1);
        AgentWork agentWork = null;

        System.runAs(csUser) {
            insert testCases;
            agentWork = TestDataGenerator.createAgentWorks(testCases, csUser).get(0);
            insert agentWork;
        
            // Validate Starting Point
            System.assertEquals(1, [SELECT Id FROM AgentWork].size());
            System.assertEquals(1, [SELECT Id FROM Case].size());
            //System.assertEquals(null, [SELECT AcceptDateTime FROM AgentWork WHERE Id = :agentWork.Id].AcceptDateTime);
            System.assertEquals(null, [SELECT AcceptDateTime FROM AgentWork LIMIT 1].AcceptDateTime);
            System.assertEquals(null, [SELECT Date_Time_Accepted__c FROM Case WHERE Id = :testCases.get(0).Id].Date_Time_Accepted__c);
        
            // Begin Test
            Test.startTest();
            
            update agentWork;
            
            // End Test
            Test.stopTest();
        }
        
        // Validate Result
        System.assertEquals(1, [SELECT Id FROM AgentWork].size());
        System.assertEquals(1, [SELECT Id FROM Case].size());
        //System.assertEquals(datetimeAccepted, [SELECT AcceptDateTime FROM AgentWork WHERE Id = :agentWork.Id].AcceptDateTime);
        System.assertEquals(datetimeAccepted, [SELECT AcceptDateTime FROM AgentWork LIMIT 1].AcceptDateTime);
        System.assertEquals(datetimeAccepted, [SELECT Date_Time_Accepted__c FROM Case WHERE Id = :testCases.get(0).Id].Date_Time_Accepted__c);
    }
My unit test fails on the line that says "insert agentWork;" (line 12) and this is how I create a AgentWork record:
public static List<AgentWork> createAgentWorks(List<Case> cases, User user) {
        ServiceChannel sc = [SELECT Id
                             FROM ServiceChannel
                             WHERE DeveloperName = 'Email'
                             LIMIT 1];
        PresenceUserConfig puc = [SELECT Id
                                  FROM PresenceUserConfig
                                  WHERE DeveloperName = 'Customer_Service'
                                  LIMIT 1];
        ServicePresenceStatus sps = [SELECT Id
                                     FROM ServicePresenceStatus
                                     WHERE DeveloperName = 'Available_for_emails'
                                     LIMIT 1];
        /*insert new ServiceChannelStatus(ServiceChannelId = sc.Id,
                                        ServicePresenceStatusId = sps.Id);
        insert new PresenceUserConfigProfile(PresenceUserConfigId = puc.id,
                                             ProfileId = user.ProfileId);*/
        List<AgentWork> agentWorks = new List<AgentWork>();

        for (Case testCase : cases) {
            agentWorks.add(new AgentWork(ServiceChannelId = sc.Id,
                                         WorkItemId = testCase.Id,
                                         UserId = user.Id));
        }

        return agentWorks;
    }
For some reason, I still get this error: "The agent's status is not associated with the channel for this work.: [ServiceChannelId]" and I'm unsure how to associate the user with the service channel.

Thanks.

Søren Nødskov
 
Jelle HendriksJelle Hendriks
So I assume you have setup OmniChannel? Your ServiceChannel is called Email which supports Cases? I guess you also have a routing configuration in place which routes cases from a specific queue to OmniChannel? If so, have you tried to make yourself member of the queue, set yourself to available through the Salesforce console, and than ran the tests? (Don't forget to insert the AgentWork with 
System.runAs()) 
This was selected as the best answer
Søren Nødskov HansenSøren Nødskov Hansen
Correct, Jelle.

However, I think the last piece missing is for me to set myself to available programmatically in the test case somehow and that's what I need help doing.

If you can help with that part, it would be great.

Thanks again.

Søren Nødskov
Jelle HendriksJelle Hendriks
Unfortuntaly I'm also still missing this piece... Therefore I did it manually in order to be able to deploy to production. 
Søren Nødskov HansenSøren Nødskov Hansen
Ah, I see.

If that works, then that's a nifty little trick to get it live.

Thanks, and should you find a way to do it programmatically, please update this thread for others to read. I will of course do the same.

/Søren Nødskov
fede martinfede martin
I'm stuck in the same situation. Is there any update on this?
Missing DebugMissing Debug
I'm with the same problem. Somebody solved this situation?
Suresh21Suresh21

Hello My Scenario also the same but i am triggering (before update, after update)

We auto assign the cases to Agents through Omni. So after seeing your posts and other related posts and speaking to Salesforce i am able to cover the test class for my AgentWork class 
See below details if it helps:

Trigger:

trigger AgentWork on AgentWork (before update, after update) {
    
    AgentWorkTriggerHandler handler=new AgentWorkTriggerHandler();
    handler.omniCaseAgentWork(Trigger.new );
}

Apex Class:

Public Class AgentWorkTriggerHandler  {

    // Define collection variables
    List<Case> caseList = new List<Case>();
    Set<Id> caseSet = new Set<Id>();
    //User u = [SELECT Id FROM User WHERE Id=:system.UserInfo.getUserId()];
    
  Public void omniCaseAgentWork (List<AgentWork> agentWorkList ){
   system.debug('omniCaseAgentWork');
    for (AgentWork aw : agentWorkList) {
            string wiId = aw.WorkItemId;
            if (wiId.left(3) == '500' ) {   // to make sure case Object 
                caseSet.add(aw.WorkItemId);
  
            }
       }
        system.debug(caseSet.size());
         
    for(case objCase:[SELECT Id,Status,Current_Milestone__c FROM Case WHERE Id IN: caseSet]){
        if(objCase.Current_Milestone__c == 'Case Assignment'){
            objCase.Status ='Work-In-Progress';
            objCase.OwnerId = UserInfo.getUserId();
            //objCase.Current_Milestone__c= 'Status Change';
            //objCase.Apex_Context__c = True;
            caseList.add(objCase);
            system.debug('£££££caseList'+ caseList);
        }    
    }
    if(caseList.size()>0)    
    update caseList;
            
 }

}

 

Test Class :

@isTest
public class AgentWorkTriggerHandlerTest {
    
    private static testMethod void AgentWorkTriggerHandler() {

    RecordType accRecordType = [Select Id from RecordType where DeveloperName =: Label.AccountRecordType And SobjectType = 'Account' limit 1];
    RecordType conRecordType = [Select Id from RecordType where DeveloperName =: Label.ContactRecordType And SobjectType = 'Contact' limit 1];
    RecordType caseRecordType = [Select Id from RecordType where DeveloperName =: Label.CaseRecordType And SobjectType = 'Case' limit 1];
    List<AgentWork> aWorkList = [Select Id, Status,WorkItemId from AgentWork Where Status= 'Assigned'];
    
    
    List<Case> WCaseList =[Select ID,Status from case where Status = 'New'];
    
    User user1 = new User();
    user1.id = UserInfo.getUserId();
    Account accRec = TestFactory.createAccount(accRecordType.Id);
    insert accRec;
        
    Contact conRec = TestFactory.createContact(conRecordType.Id, accRec.Id);
    insert conRec;

    Case caseRec = new Case();
    caseRec.RecordTypeId = caseRecordType.Id;
    caseRec.Subject = 'TTB0333333';
    caseRec.Description = 'TTB0333333';
    caseRec.OwnerId = user1.id;
    caseRec.Status = 'Work-in-Progress';
    insert caseRec;
    
    caseRec.Status ='Customer Updated';
    caseRec.Current_Milestone__c = 'Case Assignment';
    //caseRec.OwnerId = UserInfo.getUserId(); 
    
    update caseRec;
    
    //ServiceChannel SChannel= [SELECT Id FROM ServiceChannel WHERE Masterlabel = 'Case'];
    String ServiceID = Label.ServiceChannel_ID_Case;
    
    AgentWork awork = new AgentWork();
    awork.WorkItemId = caseRec.Id;
    awork.ServiceChannelId = ServiceID;
    awork.UserId = user1.id;
    insert awork;
           
    update awork;// When i update Agent Work It coverd my code coverage 
    
   
    
    }

}


Regards

Suresh 

PaulFischerSaaSPaulFischerSaaS
Hi,

To my knowledge the only way to set Omni-Channel status automatically in via the Methods for Omni-Channel Events available in the Console Toolkit. As a result this method must be used in a hidden component (i.e. Visualforce component). To my knowledge there is no way to set in Apex. As a result, the only solution at the time is to manually login to Omni-Channel and run the unit test.
Søren Nødskov HansenSøren Nødskov Hansen
Hi all

I'm just returning to the scene of the crime to give a quick update.

We're still having the same issue as initially but have learned to accept it and embraced the manual workaround suggested by Jelle.

At a later time I decided to also implement a Custom Setting which basically circumvents the Unit Test. I.e. a checkbox used to enable and disable that particular unit test method. We mainly have it "off" so that we can deploy without having to remember to log in to a specific Service Channel and if we want to do full validation of our system, we simply switch the flip and the method will again be included when running all tests.

Thanks again.

/Søren Nødskov
trail buzztrail buzz
Hi to everyone,I have a requirement like ,whenever agent can accept the case through omni channel,it will displays on case chatter history(feed).Default its shows for everything like accept or decline but i dont want to show for decline or not accepting the case in chatter history(feed ) . Is it possible to achieve ?if yes then we can do through trigger or apex or any standrad functionlaity ?
Nicolas Matias FrancoNicolas Matias Franco
Try this! It is ugly but worked on my case.

aw is an AgentWork type object.
        // This is pure shit
        for (ServiceChannel sc : [select Id from ServiceChannel limit 10]){
            try {
                aw.ServiceChannelId = sc.Id;
                insert aw;
                break;
            }catch(Exception e) {
                System.debug(LoggingLevel.INFO, 'Service channel with id '+sc.Id+' is invalid');
            }
        }

 
Laura GilLaura Gil
Hi Jelle Hendrics, all. The question is how can be such test classes deployed to target environments. Do we always need to make us or the testusers in the test classes available via Omni-Channel in the target environments, so these test classes pass the deployment process?
D CastroD Castro
Anyone had success in another way? Without updating User status to Available in the OmniChannel during class execution?