Why RunAs?

There are many situations where code will behave conditionally to the in context running user. Probably the most obvious case is where you are using the UserInfo object to check what class of user is making the request. Is this an administrator? Is it a portal user? Is it the site user? etc.

The less obvious cases include where results from queries are affected by application of the WITH SHARING keyword, perhaps you are using Apex Managed Sharing to control access or you are checking object accessibility, i.e. "does this user have access to read account?"

Anytime you switch behavior on any of these conditions you need a mechanism within testing to switch the context user to establish the expected runtime conditions to assert the behavior of your code.

Background on Apex Testing and Portals

The general concepts of testing in Apex are described on How_to_Write_Good_Unit_Tests which includes a relevant reference to the documentation on System.RunAs(User u) but this doesn't include reference to some of the complexities around setting up to run as a portal user.

If you aren't familiar with portal users, the Customer Portal section on Authenticating_Users_on_Force.com_Sites is a good primer.

Testing with a Portal User

Some things to note about the portal and test sample below:

  • The organization (your Developer Edition environment, for example) in which this is run MUST have the requisite portal licenses
  • The respective portal MUST be enabled in the organization before this test can pass.
  • The user that creates the portal user MUST have a Role.

These preconditions can cause issues for partners wishing to include portal-specific functionality in their applications. For now the guidance is to make your code execution, including tests conditional based on availability of the various elements that are made available under those conditions.

The good news is you can, once the preconditions are met, author a test which creates a portal user which you can then use to run a test:

@IsTest
private class PortalRunAsTests {

    enum PortalType { CSPLiteUser, PowerPartner, PowerCustomerSuccess, CustomerSuccess }
    
    static testmethod void usertest() {
        User pu = getPortalUser(PortalType.PowerPartner, null, true);
        System.assert([select isPortalEnabled 
                         from user 
                        where id = :pu.id].isPortalEnabled,
                      'User was not flagged as portal enabled.');       
        
        System.RunAs(pu) {
            System.assert([select isPortalEnabled 
                             from user 
                            where id = :UserInfo.getUserId()].isPortalEnabled, 
                          'User wasnt portal enabled within the runas block. ');
        }
    }
    
    public static User getPortalUser(PortalType portalType, User userWithRole, Boolean doInsert) {
    
        /* Make sure the running user has a role otherwise an exception 
           will be thrown. */
        if(userWithRole == null) {   
            
            if(UserInfo.getUserRoleId() == null) {

                UserRole r = new UserRole(name = 'TEST ROLE');
                Database.insert(r);
                
                userWithRole = new User(alias = 'hasrole', email='userwithrole@roletest1.com', userroleid = r.id,
                                    emailencodingkey='UTF-8', lastname='Testing', languagelocalekey='en_US', 
                                    localesidkey='en_US', profileid = UserInfo.getProfileId(), 
                                    timezonesidkey='America/Los_Angeles', username='userwithrole@testorg.com');
            } else {
                userWithRole = new User(Id = UserInfo.getUserId(), UserRoleId = UserInfo.getUserRoleId());
            }
            
            System.assert(userWithRole.userRoleId != null, 
                          'This test requires the target org to have at least one UserRole created. Please create a user role in this organization and try again.');
        }

        Account a;
        Contact c;
        System.runAs(userWithRole) {

            a = new Account(name = 'TEST ACCOUNT');
            Database.insert(a);
            
            c = new Contact(AccountId = a.id, lastname = 'lastname');
            Database.insert(c);

        }
        
        /* Get any profile for the given type.*/
        Profile p = [select id 
                      from profile 
                     where usertype = :portalType.name() 
                     limit 1];   
        
        String testemail = 'puser000@amamama.com';
        User pu = new User(profileId = p.id, username = testemail, email = testemail, 
                           emailencodingkey = 'UTF-8', localesidkey = 'en_US', 
                           languagelocalekey = 'en_US', timezonesidkey = 'America/Los_Angeles', 
                           alias='cspu', lastname='lastname', contactId = c.id);
        
        if(doInsert) {
            Database.insert(pu);
        }
        return pu;
    }
}