ФОН

Я работаю над управляемым пакетом, и он содержит настраиваемый объект (X__c) с одним настраиваемым полем (X__c.Fld__c). В контроллере я проверяю, есть ли у пользователя доступ на чтение как к объекту, так и к полю:

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;
  }
}

И тестовый класс.

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);
        }
    }
}

ВОПРОС

Когда я развертываю метаданные проекта в нашей упаковочной организации, происходит сбой, потому что тест verifySelectX() не проходит. Это связано с тем, что пользователь (администратор), который запускает тест при развертывании, еще не имеет доступа к объекту и его полям. Так могу ли я убедиться, что тесты пройдены? Я думал, что это распространенная проблема, но я не мог найти никакой информации об этом.

3
Eduard 25 Июн 2019 в 18:04

2 ответа

Лучший ответ

Есть несколько различных способов решить эту проблему, но наиболее распространенным является создание пользователя с разрешениями, а затем использование System.runAs для выполнения ваших тестов.

Обычно я даю разрешения своим пакетам через набор разрешений, а не через профили, поэтому я создаю пользователя, даю ему созданный набор разрешений, а затем запускаю тесты. Таким образом, я не завишу от пользователя-администратора (или каких-либо установленных профилей организации), чтобы мои тесты были успешными, и я завишу только от того, что включено в мой пакет.

Например:

@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>
  }
}
6
Sebastian Kessel 25 Июн 2019 в 18:14

Подход, который мы использовали, состоял в том, чтобы предоставить специальный класс, например. SecurityUtils с кучей статических методов, которые проверяют DescribeSObjectResult(s) или DescribeFieldResult(s), например:

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

Или

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

С вариантами доступа, создания, обновления и удаления для одного или нескольких объектов и одного или нескольких полей. Внутри эти методы проверяют, выполняется ли тест и просто ли он пройден (возвращает true или завершает работу без исключения), в противном случае он использует API Salesforce для применения реальных тестов CRUD/FLS и отвечает соответствующим образом. NB: у нас есть способ заставить код применять настоящие CRUD/FLS для небольшого числа тестов, в которых мы действительно хотим применять CRUD/FLS — тестов для самого этого класса.

Этот подход имеет некоторые недостатки, наиболее заметным из которых является то, что сканер Checkmarx не может видеть, что мы выполняем проверки CRUD/FLS, поэтому мы должны в основном помечать каждую операцию DML и запрос SOQL/SOSL как ложноположительный и объясните, что проверки были выполнены с помощью нашего служебного класса, чтобы пройти проверку безопасности для нашего управляемого пакета. (Кстати, у Salesforce есть кое-что, что выйдет зимой 2020 года [если повезет], что сделает обработку Apex CRUD/FLS намного чище и проще в реализации. тесты.)

Положительная сторона: модульные тесты ведут себя так, как будто пользователь имеет доступ ко всему и к каждой операции.

3
Phil W 25 Июн 2019 в 18:30