September 24, 2011

Is Apex System.runAs() not resetting the context for CRUD/FLS changes in profile ?

While writing an apex test case I noticed a strange behavior with System.runAs(). My tests were failing incorrectly, because system.runAs() was somehow not resetting the CRUD/FLS context for different profiles across same or different test method executions.

Here is the complete Test class with all comments and required code. To test it one can deploy this to their DE org directly, it has no dependencies.

@isTest
private class Test_System_RunAs {

/*
 This test class has two test methods, i.e.
  1. testWithSysAdmin() executes a FLS check with SYS Admin profile
  2. testWithChatterFree()  executes a FLS check with Chatter Free profile
  
  PROBLEM :
  Both of these methods doesn't execute correctly. 
  The one that executes first sets the profile context via System.runAs()
  and somehow its not reset internally for the second testMethod execution,
  this makes the testMethod executing later fail (as profile permissions mismatch)
  Though if we comment and execute both these testMethods one at a time, they will behave correct.
  So that means each of these testXXX() methods are correct, somehow context is not reset correctly
  when they execute one after another.
  
  QUESTION :
  
  Am I doing something wrong or is this some bug in System.runAs() context, I feel it takes care
  of sharing rules etc, but doesn't enforces the FLS correctly ?
  
*/

public static testMethod void testWithSysAdmin() {
  System.runAs(createMockUserForProfile('System Administrator')) {
    // This debug indicates different profiles
    System.debug('ProfileId:' +Userinfo.getProfileId());
    System.assertEquals(true, Account.BillingCity.getDescribe().isUpdateable());
  }          
}

public static testMethod void testWithChatterFree() {
  System.runAs(createMockUserForUserLicense('Chatter Free')) {
    // This debug indicates different profiles
    System.debug('ProfileId:' +Userinfo.getProfileId());
    // Chatter Free user has no permission on the Account, so it should be false
    System.assertEquals(false, Account.BillingCity.getDescribe().isUpdateable());
  }          
}

static User createMockUserForUserLicense(String licenseName) {
  Profile p = [SELECT Id FROM profile WHERE UserLicense.Name like :licenseName LIMIT 1];
  return createMockUser(p.id);
}

static User createMockUserForProfile(String profileName) {
  Profile p = [SELECT Id FROM profile WHERE name like :profileName];
  return createMockUser(p.id);
}

static User createMockUser(Id profileId) {
  User u = new User(alias = 'newUser', email='newuser@tgerm.com.test',
  emailencodingkey='UTF-8', lastname='Testing', 
  languagelocalekey='en_US', localesidkey='en_US', profileid = profileId,
  timezonesidkey='America/Los_Angeles', username= System.now().millisecond() + 'newuser@tgerm.com.test');
  insert u;
  return  u; 
}
}

Your views and comments

Looking forward for your views on this. But this seems to be a bug in System.runAs() implementation, what do you say ?

Posted the same as a question on Apex development boards here