[SalesForce] Apex Test Data – Portal Users

I cannot figure out how to create/insert a Customer Portal user record in an Apex Test.

After the necessary google hunt, I came across this piece of Salesforce documentation: Apex Testing with runAs. The Short version: use System.runAs to avoid mixed DML operations, and make sure your runAs user has a role assigned.

In the Testing with a Portal User section, it has some great example code that I worked into my existing attempts.

I've run into a lot of errors over my fight with this (Mixed DML, stuff with UserRole, etc), and up to this point have been able to solve them. But, this one I cannot seem to resolve. If I continue to beat my head against this, I am afraid my head might crack.

The error I am faced with is as follows:

System.DmlException: Insert failed. First exception on row 0; 
first error: PORTAL_NO_ACCESS, no access to portal: []

To ensure my sanity (and that I didn't miss something when I translated the example code into my own style), I copy-pasted the code block in from the salesforce documentation into a new test class, and received the same error.

This is where the error occurs.

knownPortalUser = setupUserWithContact('portal', portalProfile.Id);    

// ...

static User setupUserWithContact(String name, Id profileId) {
     Contact usersContact;

    System.runAs(userWithRole) {
        usersContact = new Contact(AccountId = knownAccount.Id, LastName = name);
        insert usersContact;
    }

    User userWithContact = getNewBaseUser(name, profileId);
    userWithContact.ContactId = usersContact.Id;

    insert userWithContact;    // FAILS HERE

    return userWithContact;
}

My best guess

My issue lies somewhere with the pre-conditions listed in the aforementioned Testing with a Portal User:

  • 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.

In my Dev Edition Org, I went to Setup | Customize | Customer Portal | Customer Portal Settings to enable the customer portal. Maybe I did this wrong? Or need to somehow give the test user I am creating permissions to this customer portal?

I do believe that licenses are a non-issue inside a test context @isTest, because any data created is just blown away in cleanup anyway.

I definitely gave the user that creates the portal user a role. It took me a while to decipher that one fully. Basically, I learned to not attempt to create my own UserRole record for testing… it was far easier to treat it as I do profiles, and find an existing one.

Anyone think they know what I'm doing wrong? or have some code that is currently working? I'm somewhat afraid that my issues are with org setup, not apex. But I have no clue what they could be…

Some stuff you might want to know:

getNewBaseUser() just returns an instantiated user object with the obligatory required fields filled out: Name, Username, etc.

userWithRole and knownAccount are setup with the following:

    // Need a user with a role assigned to be able to create portal users
    userWithRole = getNewBaseUser('hasrole', sysAdminProfile.Id);
    userWithRole.UserRoleId = someUserRole.Id;

    insert userWithRole;

    // Avoid Mixed DML
    System.runAs(userWithRole) {
        // Setup Accounts
        knownAccount = new Account(Name = 'Known Account');
        insert knownAccount;
    }

The profile & role variables are set with the following

portalProfile    = [SELECT Id  FROM Profile  WHERE Name = 'High Volume Customer Portal'  LIMIT 1];
sysAdminProfile  = [SELECT Id  FROM Profile  WHERE Name = 'System Administrator'         LIMIT 1];
someUserRole     = [SELECT Id  FROM UserRole  LIMIT 1];

Best Answer

First, you'll want to create your RunAs user who should be someone with the profile that has the permissions needed to create accounts and enable your contacts as portal users. Worst case, that can always be a System Administrator.

I usually create a Test Class Utility in most orgs that creates an Admin User for me and a "Standard User" to use for testing. It will have methods that look something like below:

/** a default user to use in System.runAs() */
private static User tstRnnr {
    get {
        if (null == tstRnnr) {
            // all test code should execute under a user we can control so as to avoid
            // surprises when deploying to different environments.
            UserRole[] roles = [SELECT Id FROM UserRole WHERE DeveloperName = 'Administrator'];
            if (roles.isEmpty() == true) {
                roles.add(new UserRole(DeveloperName = 'Administrator', Name = 'r0'));
                insert roles;
            }

            tstRnnr = new User(Alias = 'tstr01', Email='tstr01@theOrg.com', EmailEncodingKey='UTF-8', FirstName= 'TstrFrst', LastName='TstrLst01', LanguageLocaleKey='en_US', LocaleSidKey='en_US', TimeZoneSidKey='America/Los_Angeles', UserName='tstt01@theOrg.com');
            testRunner.UserRoleId = roles[0].Id;
            insert tstRnnr;
        }

        return tstRnnr;
    }

    private set;
}

As I noted, I'll do the same or similar for a user with a Standard profile

Once you have your RunAs user, a portal user needs to be created by first creating an Account, then a related contact. Once you do that, you can finally create the portal user. An account needs an own, so in the class below, I created users who would be owners. In this test class the owner of the portal user was going to be changed at a later time. I used code that looks something like below:

/* Use RunAs to create new Users with assigned profiles and roles */

integer smpl = 4; // can increase this to up to 200 or set as low as 1

    system.RunAs(U0){
    /* Create Users for Old Owners */ 

    for(i=0;i<smpls;i++){
    User u2 = new User(Alias = 'standto'+(string.valueOf(i))+'', Email='tstusro'+(string.valueOf(i))+'@thOrg.com', 
  EmailEncodingKey='UTF-8', FirstName= 'TstFrstO', LastName='TstLstO'+(string.valueOf(i))+'', LanguageLocaleKey='en_US', 
  LocaleSidKey='en_US', ProfileID = U1.ProfileID, UserRoleId=U1.UserRoleId,
  TimeZoneSidKey='America/Los_Angeles', UserName='tstusro'+(string.valueOf(i))+'@theOrg.com');

    OwnrOld.add(u2);         
    }
    if(OwnrOld.IsEmpty() == false){

      Database.SaveResult[] OwnrOldInsrtRslts = Database.Insert(OwnrOld,true);


    for(Database.SaveResult ooir: OwnrOldInsrtRslts){
      if(ooir.IsSuccess()){

        ooirIds.add(ooir.getId());

      }
    }

    system.assertEquals(ooirIds.size(),(smpls));
    }   

}// end System.RunAs(U0)

Now, on to creating the portal users...

/* Use RunAs to create new Portal Users with High Volume Portal User Profiles */

system.RunAs(U0){

/* Create Accts for Portal User Contacts to establish ownership & link to a role */

for(i=0;i<smpls;i++){
  Account CEact = new Account(Name= 'PAcctO'+ String.valueOf(i)+'', Is_Active__c = true, Region__c = 'SouthEast', OwnerID = U1.Id  );

  CEActs.add(CEact);
}

    /* insert the new Accts */

    if(CEActs.IsEmpty() == false){

      Database.SaveResult[] CEActsInsrtRslts = Database.Insert(CEActs,true);

  /* Get CE Accts Id's */

     for(Database.SaveResult actsr: CEActsInsrtRslts ){

    if(actsr.IsSuccess()){
    CEAID.add(actsr.getId());

    }

  }

  system.assertEquals(CEAID.size(),(smpls));    

    }// End if(CEActs.IsEmpty()   

/* verify save results & put into a map */

map<Id,Account> CEACTmap = new map<Id,Account>([select Id, Name, OwnerID, Region__c from Account where Id =: CEAID]);


/* Create Portal User Contacts */

    for(x=0;x<smpls;x++)  {      

        Contact CECtc = new Contact( AccountID = CEActs[x].Id, FirstName = 'PrtlFrst0', LastName = 'PrtlLstO'+ String.valueOf(x)+'', Phone = '727-755-111'+string.valueOf(x)+'', MailingStreet = '11'+String.valueOf(x) +' Prtl0'+ String.valueOf(x)+' St', MailingCity = 'PortalCty0'+String.valueOf(x)+'', MailingState = 'FL', MobilePhone = '757-743-111'+String.valueOf(x)+'', Email='portalo'+string.valueOf(x)+'@PortalTest.com' );
        CECtcs.add(CECtc);
    }


    /* insert the new Ctcs */

    if(CECtcs.IsEmpty() == false){

      Database.SaveResult[] CECtcsInsrtRslts = Database.Insert(CECtcs,true);

  /* Get CE Contact Id's */

      for(Database.SaveResult ctcr: CECtcsInsrtRslts ){

    if(ctcr.IsSuccess()){
       CIDs.add(ctcr.getId());

    }

  }

   system.assertEquals(CIDs.size(),smpls);    

    }// End if(CECtcs.IsEmpty()   

    /* verify save results and put into a map */

      map<Id,Contact> CECTCmap = new map<Id,Contact>([select Id, AccountID, FirstName, LastName, Phone, MobilePhone from Contact where Id =: CIDs]);

      /* Create Portal Users */ 

      system.assertEquals(CECTCmap.keySet().size(),smpls);

  for(i=0;i<smpls;i++){

      User u6 = new User(ContactID = CECtcs[i].Id, Alias = 'PrtlUsr'+(string.valueOf(i))+'', Email= CECtcs[i].Email, 
  EmailEncodingKey='UTF-8', FirstName= CECtcs[i].FirstName, LastName= CECtcs[i].LastName, TimeZoneSidKey='America/Chicago', 
  LanguageLocaleKey='en_US', LocaleSidKey='en_US', MobilePhone = CECtcs[i].MobilePhone, Phone = CECtcs[i].Phone,
      userName=CECtcs[i].Email, ProfileID = U5.ProfileID);

      PrtlUsrs.add(u6);         
  }


  if(PrtlUsrs.IsEmpty() == false){

      Database.SaveResult[] PrtlUsrsInsrtRslts1 = Database.Insert(PrtlUsrs,true);
  for(Database.SaveResult pu1sr: PrtlUsrsInsrtRslts1){
    if(pu1sr.IsSuccess()){
      PUIDs.add(pu1sr.getId());
    }
  }//end for

      system.assertEquals(PUIds.size(),smpls);

  }// end if(PrtlUsrs.IsEmpty()


 }// end System.RunAs(U0)

This code didn't cause any mixed DML errors.