1
 /**
 * Restful resource Patch (update record) request passes user federation Identifier
 * @param emailId
 */
@HttpPatch
global static List<ResponseWrapper> DeactivateUser_Restful_Ctl(string emailId) { 
 
// return null message if list is empty otherwise update.   
List<User> DeactivateUser = getUser(emailId);
if(DeactivateUser.isEmpty())
    return getNullMessage(); 
Database.SaveResult[] updateResult = Database.update(DeactivateUser, false); **<---------want to make this fail in test**

//if DML operation is success return success else return error & create ticket  
if(updateResult[0].isSuccess()){ 
    return getSuccessMessage(updateResult);
} else {  **<---------what i'm trying to unit test**
    createTicket(emailId, getErrorMessages(updateResult));
    return getFailMessage(updateResult);  
    }
}

So i've gotten the code tested/covered for null values on the initial query (getUser method). I've done it for a successful deactivation.......but i have a else section where sometimes the user deactivation fails (which is fine) for things like being apart of an email alert (see below)

[{"statusCode":"DEPENDENCY_EXISTS","message":"Cannot complete this operation. This user is being used by email alerts. : Email Alert ","fields":[]}]

My issue is i need to mockup a DML failure.......Now short of creating an email alert in a test class and adding a user to it how else can i mockup a DML failure of the database.update method

Unless someone knows an easier method it looks like i need to somehow make my test user fall into one of these https://help.salesforce.com/articleView?id=000322088&type=1&mode=1 ..... which i know isn't impossible to do but can you even access these from test?

The rest of my code if you want to look at it;

    @RestResource(urlMapping='/stinky/*')
global with sharing class DeactivateUser_Restful_Ctl {

    /*
    * response wrapper class
    */
    global class ResponseWrapper {
        String success;
        String message;
    }


    /**
     * Restful resource Patch (update record) request passes user federation Identifier aka their email
     * @param emailId
     */
    @HttpPatch
    global static List<ResponseWrapper> DeactivateUser_Restful_Ctl(string emailId) { // { "emailId" : "[email protected]" }
     
    // return null message if list is empty otherwise update.   
    List<User> DeactivateUser = getUser(emailId);
    if(DeactivateUser.isEmpty())
        return getNullMessage(); 
    Database.SaveResult[] updateResult = Database.update(DeactivateUser, false);
 
    //if DML operation is success return success else return error & create ticket  
    if(updateResult[0].isSuccess()){ 
        return getSuccessMessage(updateResult);
    } else {
        createTicket(emailId, getErrorMessages(updateResult));
        return getFailMessage(updateResult);  
        }
    }


    /**
     * get the record of the user to be deactivated
     * @param emailId
     */
    public static list<user> getUser(string emailId){
        List<User> userToUpdate = new List<User>();
        User[] selectedUser = [SELECT Id  
                                FROM User
                                WHERE Is_Active_Standard_User__c  = TRUE
                                AND FederationIdentifier = :emailId LIMIT 1];
        for(User record: selectedUser){
            record.IsActive = false;
            userToUpdate.add(record);
        }
        return userToUpdate;          
    }


    /**
     * get the user Id
     * @param updateResult
     */
    public static String getUserId(Database.SaveResult[] updateResult){
        return updateResult[0].getId();
    }


    /**
     * To to get the errors from the 1 record
     * @param updateResult
     */
    public static String getErrorMessages(Database.SaveResult[] updateResult){
        return  JSON.serialize(updateResult[0].getErrors()); 
    }


    /**
     * get the null message if user record is null
     *
     */
    public static list<ResponseWrapper> getNullMessage(){
        List<ResponseWrapper> responseWrapperList = new List<ResponseWrapper>();
        ResponseWrapper wrapper = new ResponseWrapper();
             wrapper.success = 'null';
             wrapper.message = 'the user record does not exist';
            responseWrapperList.add(wrapper);       
        return responseWrapperList;    
        }


    /**
     * get the failure message if dml operation failed
     * @param updateResult
     */
    public static list<ResponseWrapper> getFailMessage(Database.SaveResult[] updateResult){
    List<ResponseWrapper> responseWrapperList = new List<ResponseWrapper>();
    ResponseWrapper wrapper = new ResponseWrapper();
         wrapper.success = 'false';
         wrapper.message = 'the following error has occured for the user ' + getUserId(updateResult);
         wrapper.message = getErrorMessages(updateResult);
         wrapper.message += 'salesforce admins and developers have been notified';
        responseWrapperList.add(wrapper);       
    return responseWrapperList;   
    }
    /**
     * get the success message if update successful 
     * @param updateResult
     */
    public static list<ResponseWrapper> getSuccessMessage(Database.SaveResult[] updateResult){
        List<ResponseWrapper> responseWrapperList = new List<ResponseWrapper>();
        ResponseWrapper wrapper = new ResponseWrapper();
        wrapper.success = 'true';
        wrapper.message = 'Successfully deactivated user with ' + getUserId(updateResult);
            responseWrapperList.add(wrapper);       
        return responseWrapperList;  
    }


    /**
     * create a ticket notifying admins/devs to deactivate a user this failure happens because user is connected to something like an email alert
     * @param emailId
     */
    @future
    public static void createTicket(string emailId, string errorMessage){
        Group changeForceId = [SELECT Id FROM Group where Type = 'Queue' and NAME = 'SFDC_Changeforce' limit 1];
        Id devRecordTypeId = Schema.SObjectType.Case.getRecordTypeInfosByDeveloperName().get('Internal_SFDC_HelpDesk').getRecordTypeId();
        Case c = New Case(Subject='user deactivation request ' + emailId,
                            Description='could not deactivate user ' + emailId + ' please deactivate. : ' + errorMessage,
                            RecordTypeId = devRecordTypeId,
                            Ticket_Type__c = 'Provisioning',
                            Priority= 'P4',
                            Status='New',
                            ownerId= changeForceId.Id,
                            Application_Impacted__c='All');
                            insert c;
    }
}

edit current test class

@isTest

public with sharing class DeactivateUser_Restful_Test {

//test null response
@isTest
static void Null_Restful_Test() {
    string emailId = '[email protected]';
    RestRequest req = restTest(emailId);
    RestResponse res = new RestResponse();

    RestContext.request = req;
    RestContext.response = res;
    makeData();
    test.startTest();
    DeactivateUser_Restful_Ctl.ResponseWrapper[] rep = DeactivateUser_Restful_Ctl.DeactivateUser_Restful_Ctl(emailId);
    test.stoptest();
}


//test response success
@isTest
static void Success_Restful_Test(){
    string emailId =  '[email protected]';
    RestRequest req = restTest(emailId);
    RestResponse res = new RestResponse();

    RestContext.request = req;
    RestContext.response = res;
    makeData();

    test.startTest();
    DeactivateUser_Restful_Ctl.ResponseWrapper[] rep = DeactivateUser_Restful_Ctl.DeactivateUser_Restful_Ctl(emailId);
    test.stoptest();
}


//method to create rest request
static RestRequest restTest(string emailId){
    RestRequest request = new RestRequest();
    request.requestURI = System.Url.getOrgDomainUrl().toExternalForm() +'/services/apexrest/User/*';
    request.httpMethod = 'PATCH';
    request.requestBody = Blob.valueOf(emailId);
    return request;
}


//test data for user creation
static void makeData(){
    Profile userProfile = [SELECT Id FROM Profile WHERE Name='Parts'];
    user u = new user(alias = 'al36test', FederationIdentifier ='[email protected]',  
            Email='[email protected]', Department__c = 'Sales', 
            EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US', 
            LocaleSidKey='en_US', ProfileId = userProfile.Id,
            TimeZoneSidKey='America/Los_Angeles', UserName='[email protected]');
        insert u;
}

}

1 Answer 1

2

There are several different ways you can approach this. Here are a couple of them:

  1. Split the DML into a separate virtual method and override this method in the one test that needs to simulate a DML error, using a test-specific class that extends the DeactivateUser_Restful_Ctl class that throws an error from the DML method. If you need to return "believable" SaveResults with errors you can do that using a JSON deserialize dodge (see below).
  2. Provide some static detail, used in a Trigger on User, accessible through a specific class that is only considered in the User trigger when Test.isRunningTest() is true and that, when the static detail matches the test configuration (e.g. the user's name is a specific value held in this static detail), has the trigger throw an exception or add an error to the User record etc.

Here's an example of faking a problem against a given User object when updating IsActive (for if you take the first approach, above):

return new List<Database.SaveResult> {
    (Database.SaveResult) JSON.deserialize(
        '{"errors": [{"fields": ["IsActive"], "message": "Whoops!"}], "id": "' +
        user.Id +
        '", "success": false}', Database.SaveResult.class);

(Assuming access to the (single) "user" record with an ID set.)

4
  • So i've never done anything with virtual methods/class, not that i wont try #1....for now i'm going with #2 but i'd like to update to to try #1..... so are you saying place the entire dml statement into it's own class because i've only seen references to virtual classes not individual methods with in class.
    – ensir
    Mar 31, 2021 at 20:49
  • 1
    You declare the class as virtual, then declare the method containing the DML as virtual. In the test you create an inner class (outside any methods) extending the class to test, and define the DML method, with "override". This is quite easy to do. See developer.salesforce.com/docs/atlas.en-us.apexcode.meta/…. The test for failure creates an instance of the inner class instead of the class under test, just for this one test.
    – Phil W
    Mar 31, 2021 at 21:03
  • so i created this class in my test that you can see in my updated main post. My question is how do i actually use that with my tests? You can see with my test.startTest() i need to actually pass a parameter to the webservice....so do i do that and also use the override? Because i need to test that if(database.SaveResults[].isSuccess){do stuff im not testing}else {the stuff i am testing} ahhhhhhhhhhhhh nvm http has to be static and virtual methods cant be static
    – ensir
    Apr 7, 2021 at 21:25
  • Are you still stuck? Do you need to ask another question? Best to ask it as a separate question.
    – Phil W
    Apr 8, 2021 at 6:44

You must log in to answer this question.

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