3

BACKGROUND

I am working on a managed package and it contains a custom object (X__c) with one custom field (X__c.Fld__c). In the controller I check if a user has Read access to both the object and field:

MyController.cls

public class MyController
{
  public MyController() {}

  public List<X__c> selectX()
  {
     if (X__c.SObjectType.getDescribe().isAccessible()) {
       if (X__c.Fld__c.getDescribe().isAccessible())
       {
         return [SELECT Id, Fld__c FROM X__c];
       }
     }

     return null;
  }
}

And the test class.

MyControllerTest.cls

public class MyControllerTest
{
    @isTest
    static void verifySelectX()
    {
        // Given
        X__c[] xRecords =
            (List<X__c>) TestFactory.create(new X__c(), 2);

        // When
        xRecords = new MyController().selectX();

        // Then
        System.assertNotEquals(null, xRecords);
        System.assertEquals(2, xRecords.size());
    }

    @isTest
    static void verifySelectXNoAccess()
    {
        // Given
        X__c[] xRecords =
            (List<X__c>) TestFactory.create(new X__c(), 2);

        // Create a user which will not have access to the test object type
        User testUser = createUser('Chatter External');
        if (testUser == null) {
            // Abort the test if unable to create a user with low enough acess
            return;
        }

        // When
        System.runAs(testUser)
        {
            xRecords = new MyController().selectX();

            // Then
            System.assertEquals(null, xRecords);
        }
    }
}

QUESTION

When I deploy the project metadata to our packaging org, it fails because the verifySelectX() test doesn't pass. It's due to the user (admin) who runs the test on deployment does not have access to the object and its fields yet. So can I make sure that the tests pass? I thought this is a common problem, but I couldn't find any info on it.

6

There are a few different ways to attack this problem, but the most common is to create a user with permissions, and then use System.runAs to execute your tests.

I normally grant permissions to my packages via permset and not profiles, so I create a user, give it a permission set that I created, and then run the tests. That way I don't depend on the admin user (or any installed org's profiles) for my tests to be successful and I only depend on things that are included in my package.

For example:

@IsTest
static void myTestPermSet() {
  User myUser = new User(<UserPropertiesGoHere>);
  insert u;

  PermissionSetAssignment psa = new PermissionSetAssignment (PermissionSetId = myPermissionSetId, AssigneeId = u.id);

  insert psa;    

  System.RunAs(u) {
    Test.startTest();

    <test your code here>

    Test.stopTest();

    <asserts go here>
  }
}
| improve this answer | |
3

The approach we took was to provide a special class, e.g. SecurityUtils, with a bunch of static methods on it that take the DescribeSObjectResult(s) or DescribeFieldResult(s) to be checked, e.g.:

// Returns true if the user has access to all the specified fields
public static Boolean canAccessFields(DescribeFieldResult[] fields)

or

// Throws an exception if the user doesn't have access to all the specified fields
public static void mustAccessFields(DescribeFieldResult[] fields)

With variants for access, create, update and delete against one or several objects and one or several fields. Internally these methods check to see if a test is running and if it is simply passes (return true or exit without exception), otherwise it uses the Salesforce API to apply the real CRUD/FLS tests and responds accordingly. NB: We have a way to force the code to apply real CRUD/FLS for the small number of tests we do want CRUD/FLS to be applied in - the tests for this class itself.

This approach has some down-sides, the most notable being that the Checkmarx scanner can't see that we do the CRUD/FLS checks, so we have to basically mark every single DML operation and SOQL/SOSL query as being a False Positive and explain that checks were done via our util class in order to pass the security review for our managed package. (BTW, Salesforce has some goodies coming out Winter '20 [with luck] that will make Apex handling of CRUD/FLS much cleaner and easier to implement. However, you'll still want to be able to mock out those new features in unit tests.)

The up-side: unit tests behave as if the user has access to everything and every operation.

| improve this answer | |

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.