2

I have a test class that, as one of the steps, inserts a portal user. After that's done, I use System.RunAs to insert a case as the portal user. The insertion of the case fires a trigger that includes a step to query the user object and then loop through it until it finds the current user.

I'm expecting that query to return the current user, but it isn't. Interestingly, my debug statements indicate that the user was indeed created.

Any ideas for why my query isn't returning the user created in the test class?

Test class:

@IsTest public class Case_Partner_AccountTest { 

    public static Account getAcc(){
        Account a = new Account(Name = 'Test Account');
        insert a;
        return a;
    } 

    // Creates and returns a contact
    public static Contact getCon(){
        Contact c = new Contact(LastName = 'Testing', AccountId = getAcc().Id);
        insert c;
        return c;
}

    // Creates and returns a user with parameter specified profile
    public static User getUser(boolean hasContact, String profileName){
        Profile pr = [SELECT Id FROM Profile WHERE Name = :profileName];
        User u = new User();
        u.FirstName = 'Ivanna';
        u.LastName = 'Testmor';
        u.Email = 'itestmor@xyz123.com.testuser';   
        u.Username = 'itestmor@xyz123.com.testuser';
        u.Alias = 'itestmor';
        u.CommunityNickname = 'itestmor';
        u.TimeZoneSidKey = 'GMT';
        u.LanguageLocaleKey = 'en_US';
        u.EmailEncodingKey = 'UTF-8';
        u.LocaleSidKey = 'en_US';
        if(hasContact){
            Contact c = getCon();
            u.ContactId = c.Id;
            u.PortalRole = 'Worker';
        }
        u.ProfileId = pr.Id;
        insert u;
        return u;
}

    @IsTest public static void test01(){
        User u = getUser(true, 'Custom Portal Support User');
        Case c = new Case();
        Test.startTest();
        System.runAs(u){
            insert c;
        }
        Test.stopTest();

Here's the class that gets called from the Case trigger. You can see that is should loop through the User object and find the user. The FOR loop is failing to find the user created in the test class, even though the debug statement in line 5 shows that a user ID exists:

public class CaseTrigger_Partner_Account {
    public static void execute(List<Case> tNew, boolean tIsInsert){
        User currentUser;
        Id uId = userInfo.getUserId();
        System.debug('uId is ' + uId);
        for(User u : GlobalUtils.getUserIdMap().values()){
            if(u.Id == uId) {
                currentUser = u;
                break;
            }
        }

GlobalUtils.getUserIdMap (this is called in the class above):

private static Map<Id,User> UserIdMap;
public static Map<Id,User> getUserIdMap(){
    if(UserIdMap == NULL)
        UserIdMap = new Map<Id,User>([Select Id, Name, UserRoleId, ManagerId, UserName, usertype, ContactId, Contact.AccountId, Contact.Account.Name, FirstName, LastName, IsActive FROM User]);
    return UserIdMap;
}

2 Answers 2

2

Static variables remain set for the duration of the transaction. If your Contact trigger also makes use of GlobalUtils.getUserIdMap, then your query will get lazy loaded before you create the user. Then, when you run your Case trigger, the stale data cache is used, and your user is not found.

You could get around this by setting the map to null after, but it is private. Another workaround would be to use @TestSetup to create your data. Setup methods annotated in such a way run in a separate transaction, so you will not run into issues with static lazy loading. It would look something like:

static final String aliasWithContact = 'withCon';
static final String aliasWithoutContact = 'noCon';
// hopefully the above are unique to your org

@TestSetup
static void setup()
{
    Contact record = new Contact(/*data*/);
    insert record;
    User u1 = new User(Alias=aliasWithContact, /*add other data*/);
    User u2 = new User(Alias=aliasWithoutContact, /*add other data*/);
    insert new List<User> { u1, u2 };
}

static testmethod void testUserWithContact()
{
    system.runAs([SELECT Id FROM User WHERE Alias = :aliasWithContact])
    {
        // logic here
    }
}
static testmethod void testUserWithoutContact()
{
    system.runAs([SELECT Id FROM User WHERE Alias = :aliasWithoutContact])
    {
        // logic here
    }
}
1
  • Thanks @AdrianLarson. That was exactly the issue - our contact trigger does indeed utilize the GlobalUtils.getUserIdMap. May 30, 2017 at 20:11
1

In addition to Adrian' answer:

If you are using "with sharing" in your code, by default:

organization-wide default setting of Private for external access to the User object for Community or Portal users.

so the runAs user may not be able see the user object

https://help.salesforce.com/articleView?id=security_sharing_owd_external_user_settings.htm&language=en_US&type=0

  1. Deselect the Portal User Visibility checkbox to allow users to be seen by only themselves and their superiors. Or select the checkbox to let portal users be seen by all other portal users within the same account.

  2. For Community User Visibility, deselect the checkbox to allow users to be seen only by themselves and their superiors. Select the checkbox to allow community users to be seen by all other users in their communities.

Selecting either of these options is a quick way of overriding an organization-wide default setting of Private for external access to the User object for Community or Portal users.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.