Skip to main content
added 56 characters in body; edited title
Source Link
Oscar Tang
  • 625
  • 7
  • 18

Using System.runAs RestResponse does not return expected statuc code in Apex Rest unit test

Can I use SystemApex Rest returns 200 OK status code when directly access its Rest endpoint.runAs to test However, in Apex Rest classes with different users?test class. RestResponse hooked in RestContext does not contains the status code (see below debug log).

@isTest
static void testCreateUser() {
    RestRequest req = new RestRequest();
    RestResponse res = new RestResponse();

    TestUtil.initTestData();

    User customerAdmin = [SELECT Id, AccountId FROM User WHERE username = '[email protected]'];

    String requestBody = '{ "firstName": "Test", "lastName": "Case 2", "email": "[email protected]" }';

    req.requestURI = URL.getSalesforceBaseUrl() + '/services/apexrest/MyId';
    req.httpMethod = 'POST';
    req.requestBody = Blob.valueOf(requestBody);

    Test.startTest();
    RestContext.request = req;
    RestContext.response = res;

    System.runAs(customerAdmin) {
        UserManagementRestController.createMyId();

        System.debug(RestContext.requestreq);
        System.debug(RestContext.responseres);
    }
    
    Test.stopTest();
    // Seems that Salesforce does notSomething supportgoes mixingwrong Systemhere.runAs and RestContext, noNo content in RestContext.response
    // Tempoary disable assertion on response
    //System.assertEquals(200, res.statusCode);

    List<User> users = [SELECT FirstName, LastName, Username, AccountId FROM User WHERE Email = '[email protected]'];
    System.assertEquals(1, users.size());

    User u = users[0];
    System.assertEquals('Test', u.FirstName);
    System.assertEquals('Case 2', u.LastName);
    System.assertEquals('[email protected]', u.Username);
    System.assertEquals(customerAdmin.AccountId, u.AccountId);
}

In the debug log we can see the RestRequest is correctly set. However the RestResponse does not have any value in it. Would expect statusCode=200

I am wondering if Salesforce supports mixing System.runAsSo what is the reason of getting different result from the test class and RestContext in the Apexactual Rest unit test. I needendpoint? Any way to test the exact behaviour of the restApex Rest endpoint with different users. Is there any other way to goin a unit test class?

Using System.runAs in Apex Rest unit test

Can I use System.runAs to test Apex Rest classes with different users?

@isTest
static void testCreateUser() {
    RestRequest req = new RestRequest();
    RestResponse res = new RestResponse();

    TestUtil.initTestData();

    User customerAdmin = [SELECT Id, AccountId FROM User WHERE username = '[email protected]'];

    String requestBody = '{ "firstName": "Test", "lastName": "Case 2", "email": "[email protected]" }';

    req.requestURI = URL.getSalesforceBaseUrl() + '/services/apexrest/MyId';
    req.httpMethod = 'POST';
    req.requestBody = Blob.valueOf(requestBody);

    Test.startTest();
    RestContext.request = req;
    RestContext.response = res;

    System.runAs(customerAdmin) {
        UserManagementRestController.createMyId();

        System.debug(RestContext.request);
        System.debug(RestContext.response);
    }
    
    Test.stopTest();
    // Seems that Salesforce does not support mixing System.runAs and RestContext, no content in RestContext.response
    // Tempoary disable assertion on response
    //System.assertEquals(200, res.statusCode);

    List<User> users = [SELECT FirstName, LastName, Username, AccountId FROM User WHERE Email = '[email protected]'];
    System.assertEquals(1, users.size());

    User u = users[0];
    System.assertEquals('Test', u.FirstName);
    System.assertEquals('Case 2', u.LastName);
    System.assertEquals('[email protected]', u.Username);
    System.assertEquals(customerAdmin.AccountId, u.AccountId);
}

In the debug log we can see the RestRequest is correctly set. However the RestResponse does not have any value in it.

I am wondering if Salesforce supports mixing System.runAs and RestContext in the Apex Rest unit test. I need to test the behaviour of the rest endpoint with different users. Is there any other way to go?

RestResponse does not return expected statuc code in Apex Rest unit test

Apex Rest returns 200 OK status code when directly access its Rest endpoint. However, in Apex Rest test class. RestResponse hooked in RestContext does not contains the status code (see below debug log).

@isTest
static void testCreateUser() {
    RestRequest req = new RestRequest();
    RestResponse res = new RestResponse();

    TestUtil.initTestData();

    User customerAdmin = [SELECT Id, AccountId FROM User WHERE username = '[email protected]'];

    String requestBody = '{ "firstName": "Test", "lastName": "Case 2", "email": "[email protected]" }';

    req.requestURI = URL.getSalesforceBaseUrl() + '/services/apexrest/MyId';
    req.httpMethod = 'POST';
    req.requestBody = Blob.valueOf(requestBody);

    Test.startTest();
    RestContext.request = req;
    RestContext.response = res;

    System.runAs(customerAdmin) {
        UserManagementRestController.createMyId();

        System.debug(req);
        System.debug(res);
    }
    
    Test.stopTest();
    // Something goes wrong here. No content in RestContext.response
    // Tempoary disable assertion on response
    //System.assertEquals(200, res.statusCode);

    List<User> users = [SELECT FirstName, LastName, Username, AccountId FROM User WHERE Email = '[email protected]'];
    System.assertEquals(1, users.size());

    User u = users[0];
    System.assertEquals('Test', u.FirstName);
    System.assertEquals('Case 2', u.LastName);
    System.assertEquals('[email protected]', u.Username);
    System.assertEquals(customerAdmin.AccountId, u.AccountId);
}

In the debug log we can see the RestRequest is correctly set. However the RestResponse does not have any value in it. Would expect statusCode=200

So what is the reason of getting different result from the test class and the actual Rest endpoint? Any way to test the exact behaviour of Apex Rest endpoint in a unit test class?

added 3799 characters in body
Source Link
Oscar Tang
  • 625
  • 7
  • 18
@RestResource(urlMapping='/MyId/*')
global class UserManagementRestController {

    @HttpPost
    global static void createMyId() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;

        User requestUser = [SELECT Id, AccountId, Email FROM User WHERE Id = :UserInfo.getUserId()];

        // somecheck logicif reqest user is customer admin
        Set<String> userPermissions = new Set<String>(UserPermissionRemoter.getUserPermission(requestUser.Id));
        if(!userPermissions.contains(UserPermissionMapper.customerAdminPermissionName)) {
            responseBadRequest('Only Customer Admin can perform this action', res);
            return;
        }

        MyId hid;
        try {
            hid = (MyId)JSON.deserialize(req.requestBody.toString(), MyId.class);
        } catch (JSONException e) {
            responseBadRequest(e.getMessage(), res);
            return;
        }
        System.debug('hid:');
        System.debug(hid);

        String email = hid.email;

        if(!isValidateEmail(email)) {
            responseBadRequest('Invalidate Email', res);
            return;
        }

        // check if new email is in the same domain with super user
        String[] splitEmail = email.split('@');
        String[] splitCurrentUserEmail = requestUser.Email.split('@');
        if(splitEmail[1].compareTo(splitCurrentUserEmail[1]) != 0) {
            responseBadRequest('Email domain mismatched. The new user email must have the same domain as the customer admin’s email.', res);
            return;
        }

        Savepoint createNewUserSavePoint = Database.setSavepoint();
        try {
            Contact newCt = new Contact();
            newCt.FirstName = hid.firstName;
            newCt.LastName = hid.lastName;
            newCt.Email = hid.email;
            newCt.AccountId = requestUser.AccountId;
            insert newCt;

            // Directly use corporate email
            String username = newCt.Email;

            // gen nickname
            DateTime currentTime = System.now();
            Long timeInMili = currentTime.getTime()/1000;
            String nickname = splitEmail[0]+timeInMili;

            // gen alias
            String alias = username;
            if(alias.length() > 8) {
                alias = alias.substring(0, 8);
            }

            User newUsr = new User();
            newUsr.FirstName = newCt.firstName;
            newUsr.LastName = newCt.lastName;
            newUsr.Email = newCt.email;
            newUsr.CommunityNickname = nickname;
            newUsr.profileId = getCommunityProfileId();
            newUsr.contactId = newCt.Id;
            newUsr.UserName = username;
            newUsr.Alias = alias;
            newUsr.TimeZoneSidKey = 'America/Los_Angeles';
            newUsr.LocaleSidKey = 'en_US';
            newUsr.EmailEncodingKey = 'ISO-8859-1';
            newUsr.LanguageLocaleKey = 'en_US';
            newUsr.IsActive = true;

            insert newUsr;
        } catch (system.Dmlexception e) {
            Database.rollback(createNewUserSavePoint);
            if(String.valueOf(e).contains('DUPLICATE_USERNAME')||String.valueOf(e).contains('DUPLICATE')){
                responseBadRequest('An account with this email already exists', res);
                return;
            }
            else{
                responseBadRequest(String.valueOf(e), res);
                return;
            }
        }
    }

    private static Boolean isValidateEmail(String email) {
        //Pattern p = Pattern.compile('^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$');
        //Matcher m = p.matcher(email);

        // according to SFDC support: https://help.salesforce.com/apex/HTViewSolution?id=000170904&language=en_US
        Pattern p = Pattern.compile('^[A-Z0-9._%+-/!#$%&\'*=?^_`{|}~]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$');
        Matcher m = p.matcher(email.toUpperCase());
        return m.matches();
    }

    private static Id getCommunityProfileId() {
        Profile p = [SELECT Id FROM Profile WHERE Name = 'My Customer Portal User' limit 1][0];
        return p.Id;
    }

    private static void responseBadRequest(String message, RestResponse res) {
        res.responseBody = Blob.valueOf(message);
        res.statusCode = 400;
        return;
    }

    public class MyId {
        public String firstName;
        public String lastName;
        public String email;

        public Boolean isCustomerAdmin;
        public Boolean isActive;
    }
}
@RestResource(urlMapping='/MyId/*')
global class UserManagementRestController {

    @HttpPost
    global static void createMyId() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;

        // some logic......
    }
}
@RestResource(urlMapping='/MyId/*')
global class UserManagementRestController {

    @HttpPost
    global static void createMyId() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;

        User requestUser = [SELECT Id, AccountId, Email FROM User WHERE Id = :UserInfo.getUserId()];

        // check if reqest user is customer admin
        Set<String> userPermissions = new Set<String>(UserPermissionRemoter.getUserPermission(requestUser.Id));
        if(!userPermissions.contains(UserPermissionMapper.customerAdminPermissionName)) {
            responseBadRequest('Only Customer Admin can perform this action', res);
            return;
        }

        MyId hid;
        try {
            hid = (MyId)JSON.deserialize(req.requestBody.toString(), MyId.class);
        } catch (JSONException e) {
            responseBadRequest(e.getMessage(), res);
            return;
        }
        System.debug('hid:');
        System.debug(hid);

        String email = hid.email;

        if(!isValidateEmail(email)) {
            responseBadRequest('Invalidate Email', res);
            return;
        }

        // check if new email is in the same domain with super user
        String[] splitEmail = email.split('@');
        String[] splitCurrentUserEmail = requestUser.Email.split('@');
        if(splitEmail[1].compareTo(splitCurrentUserEmail[1]) != 0) {
            responseBadRequest('Email domain mismatched. The new user email must have the same domain as the customer admin’s email.', res);
            return;
        }

        Savepoint createNewUserSavePoint = Database.setSavepoint();
        try {
            Contact newCt = new Contact();
            newCt.FirstName = hid.firstName;
            newCt.LastName = hid.lastName;
            newCt.Email = hid.email;
            newCt.AccountId = requestUser.AccountId;
            insert newCt;

            // Directly use corporate email
            String username = newCt.Email;

            // gen nickname
            DateTime currentTime = System.now();
            Long timeInMili = currentTime.getTime()/1000;
            String nickname = splitEmail[0]+timeInMili;

            // gen alias
            String alias = username;
            if(alias.length() > 8) {
                alias = alias.substring(0, 8);
            }

            User newUsr = new User();
            newUsr.FirstName = newCt.firstName;
            newUsr.LastName = newCt.lastName;
            newUsr.Email = newCt.email;
            newUsr.CommunityNickname = nickname;
            newUsr.profileId = getCommunityProfileId();
            newUsr.contactId = newCt.Id;
            newUsr.UserName = username;
            newUsr.Alias = alias;
            newUsr.TimeZoneSidKey = 'America/Los_Angeles';
            newUsr.LocaleSidKey = 'en_US';
            newUsr.EmailEncodingKey = 'ISO-8859-1';
            newUsr.LanguageLocaleKey = 'en_US';
            newUsr.IsActive = true;

            insert newUsr;
        } catch (system.Dmlexception e) {
            Database.rollback(createNewUserSavePoint);
            if(String.valueOf(e).contains('DUPLICATE_USERNAME')||String.valueOf(e).contains('DUPLICATE')){
                responseBadRequest('An account with this email already exists', res);
                return;
            }
            else{
                responseBadRequest(String.valueOf(e), res);
                return;
            }
        }
    }

    private static Boolean isValidateEmail(String email) {
        //Pattern p = Pattern.compile('^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$');
        //Matcher m = p.matcher(email);

        // according to SFDC support: https://help.salesforce.com/apex/HTViewSolution?id=000170904&language=en_US
        Pattern p = Pattern.compile('^[A-Z0-9._%+-/!#$%&\'*=?^_`{|}~]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$');
        Matcher m = p.matcher(email.toUpperCase());
        return m.matches();
    }

    private static Id getCommunityProfileId() {
        Profile p = [SELECT Id FROM Profile WHERE Name = 'My Customer Portal User' limit 1][0];
        return p.Id;
    }

    private static void responseBadRequest(String message, RestResponse res) {
        res.responseBody = Blob.valueOf(message);
        res.statusCode = 400;
        return;
    }

    public class MyId {
        public String firstName;
        public String lastName;
        public String email;

        public Boolean isCustomerAdmin;
        public Boolean isActive;
    }
}
added 713 characters in body
Source Link
Oscar Tang
  • 625
  • 7
  • 18

Here is my code:test method. The containing class is also annotated with @isTest

@isTest
static void testCreateUser() {
    RestRequest req = new RestRequest();
    RestResponse res = new RestResponse();

    TestUtil.initTestData();

    User customerAdmin = [SELECT Id, AccountId FROM User WHERE username = '[email protected]'];

    String requestBody = '{ "firstName": "Test", "lastName": "Case 2", "email": "[email protected]" }';

    req.requestURI = URL.getSalesforceBaseUrl() + '/services/apexrest/MyId';
    req.httpMethod = 'POST';
    req.requestBody = Blob.valueOf(requestBody);

    Test.startTest();
    RestContext.request = req;
    RestContext.response = res;

    System.runAs(customerAdmin) {
        MyRestControllerUserManagementRestController.createMyId();

        System.debug(RestContext.request);
        System.debug(RestContext.response);
    }
    
    Test.stopTest();
    // Seems that Salesforce does not support mixing System.runAs and RestContext, no content in RestContext.response
    // Tempoary disable assertion on response
    //System.assertEquals(200, res.statusCode);

    List<User> users = [SELECT FirstName, LastName, Username, AccountId FROM User WHERE Email = '[email protected]'];
    System.assertEquals(1, users.size());

    User u = users[0];
    System.assertEquals('Test', u.FirstName);
    System.assertEquals('Case 2', u.LastName);
    System.assertEquals('[email protected]', u.Username);
    System.assertEquals(customerAdmin.AccountId, u.AccountId);
}

My Apex REST class:

@RestResource(urlMapping='/MyId/*')
global class UserManagementRestController {

    @HttpPost
    global static void createMyId() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;

        // some logic......
    }
}

Here is my code:

    RestRequest req = new RestRequest();
    RestResponse res = new RestResponse();

    TestUtil.initTestData();

    User customerAdmin = [SELECT Id, AccountId FROM User WHERE username = '[email protected]'];

    String requestBody = '{ "firstName": "Test", "lastName": "Case 2", "email": "[email protected]" }';

    req.requestURI = URL.getSalesforceBaseUrl() + '/services/apexrest/MyId';
    req.httpMethod = 'POST';
    req.requestBody = Blob.valueOf(requestBody);

    Test.startTest();
    RestContext.request = req;
    RestContext.response = res;

    System.runAs(customerAdmin) {
        MyRestController.createMyId();

        System.debug(RestContext.request);
        System.debug(RestContext.response);
    }
    
    Test.stopTest();

Here is my test method. The containing class is also annotated with @isTest

@isTest
static void testCreateUser() {
    RestRequest req = new RestRequest();
    RestResponse res = new RestResponse();

    TestUtil.initTestData();

    User customerAdmin = [SELECT Id, AccountId FROM User WHERE username = '[email protected]'];

    String requestBody = '{ "firstName": "Test", "lastName": "Case 2", "email": "[email protected]" }';

    req.requestURI = URL.getSalesforceBaseUrl() + '/services/apexrest/MyId';
    req.httpMethod = 'POST';
    req.requestBody = Blob.valueOf(requestBody);

    Test.startTest();
    RestContext.request = req;
    RestContext.response = res;

    System.runAs(customerAdmin) {
        UserManagementRestController.createMyId();

        System.debug(RestContext.request);
        System.debug(RestContext.response);
    }
    
    Test.stopTest();
    // Seems that Salesforce does not support mixing System.runAs and RestContext, no content in RestContext.response
    // Tempoary disable assertion on response
    //System.assertEquals(200, res.statusCode);

    List<User> users = [SELECT FirstName, LastName, Username, AccountId FROM User WHERE Email = '[email protected]'];
    System.assertEquals(1, users.size());

    User u = users[0];
    System.assertEquals('Test', u.FirstName);
    System.assertEquals('Case 2', u.LastName);
    System.assertEquals('[email protected]', u.Username);
    System.assertEquals(customerAdmin.AccountId, u.AccountId);
}

My Apex REST class:

@RestResource(urlMapping='/MyId/*')
global class UserManagementRestController {

    @HttpPost
    global static void createMyId() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;

        // some logic......
    }
}
Source Link
Oscar Tang
  • 625
  • 7
  • 18
Loading