Loading
Настройка и обслуживание организации Salesforce
Содержание
Выбрать фильтры

          Результаты отсутствуют
          Результаты отсутствуют
          Ниже приведены некоторые советы по поиску.

          Проверьте орфографию ключевых слов.
          Воспользуйтесь более общим поисковым запросом.
          Выберите несколько фильтров для расширения области поиска.

          Выполните поиск по всей справке Salesforce.
          Тестирование Apex расширенной политики безопасности транзакций

          Тестирование Apex расширенной политики безопасности транзакций

          Написание устойчивых тестов рекомендуется в программотехнике для обеспечения ожидаемого алгоритма кода и определения ошибок до того, как это сделают ваши пользователи или клиенты. Создание тестов для кода Apex политики безопасности транзакций еще более важно, поскольку он выполняется во время важных действий пользователя в организации Salesforce. Например, ошибка политики LoginEvent, не обнаруженная тестом, может привести к блокировке пользователей организации, а этой ситуации нужно избежать.

          Требуемые версии

          Доступно в версиях Salesforce Classic (недоступно во всех организациях) и Lightning Experience.

          Доступно в версиях: Enterprise Edition, Unlimited Edition и Developer Edition.

          Требуется приобретение подписок на дополнительные компоненты Salesforce Shield или Salesforce Event Monitoring.

          Предупреждение!
          Предупреждение! Используйте версию API 47.0 или более позднюю при написании тестов Apex для расширенных политик безопасности транзакций.

          При тестировании кода Apex посредством симуляции набора условий вы по определению создаете поблочные тесты. Но написания поблочных тестов недостаточно. Проконсультируйтесь с рабочими бизнес группами и группами по безопасности, чтобы определить все способы использования. Потом создайте универсальный план теста, которые воссоздает фактическую работу пользователей, используя данные теста в безопасной среде sandbox. План теста обычно содержит как тестирование вручную, так и автоматическое с использованием внешних инструментов, например, Selenium.

          Рассмотрим несколько поблочных тестов. Ниже указана политика Apex, которую необходимо протестировать.

          global class LeadExportEventCondition implements TxnSecurity.EventCondition {
              public boolean evaluate(SObject event) {
                  switch on event{
                      when ApiEvent apiEvent {
                          return evaluate(apiEvent.QueriedEntities, apiEvent.RowsProcessed);
                      }
                      when ReportEvent reportEvent {
                          return evaluate(reportEvent.QueriedEntities, reportEvent.RowsProcessed);
                      }
                      when ListViewEvent listViewEvent {
                          return evaluate(listViewEvent.QueriedEntities, listViewEvent.RowsProcessed);
                      }
                      when null {
                           return false;   
                      }
                      when else {
                          return false;
                      }
                  }
              }
          
              private boolean evaluate(String queriedEntities, Decimal rowsProcessed){
                  if (queriedEntities.contains('Lead') && rowsProcessed > 2000){
                      return true;
                  }
                  return false;
              }
          }

          Планирование и написание тестов

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

          Положительные способы тестирования
          Если метод evaluate получает... И... Потом метод evaluate возвращает...
          Объект ApiEvent ApiEvent содержит Lead в поле QueriedEntities и число больше 2000 в поле RowsProcessed true
          Объект ReportEvent ReportEvent содержит Lead в поле QueriedEntities и число больше 2000 в поле RowsProcessed true
          Объект ListViewEvent ListViewEvent содержит Lead в поле QueriedEntities и число больше 2000 в поле RowsProcessed true
          Объект любого события Событие не содержит Lead в поле QueriedEntities и содержит число больше 2000 в поле RowsProcessed false
          Объект любого события Событие имеет Lead в поле QueriedEntities и число меньше или равно 2000 в поле RowsProcessed false
          Объект любого события Событие не содержит Lead в поле QueriedEntities и содержит число меньше или равное 2000 в поле RowsProcessed false
          Отрицательные способы тестирования
          Если метод evaluate получает... И... Потом метод evaluate возвращает...
          Объект LoginEvent (условия отсутствуют) false
          Нулевое значение (условия отсутствуют) false
          Объект ApiEvent Поле QueriedEntities нулевое false
          Объект ReportEvent Поле RowsProcessed нулевое false

          Ниже указан код тестирования Apex, внедряющий все эти случаи использования.

          /**
           * Tests for the LeadExportEventCondition class, to make sure that our Transaction Security Apex 
           * logic handles events and event field values as expected.
           **/
           @isTest
           public class LeadExportEventConditionTest {
           
              /**
               * ------------ POSITIVE TEST CASES ------------
               ** /
           
               /**
                * Positive test case 1: If an ApiEvent has Lead as a queried entity and more than 2000 rows 
                * processed, then the evaluate method of our policy's Apex should return true.
                **/ 
                static testMethod void testApiEventPositiveTestCase() {
                    // set up our event and its field values
                    ApiEvent testEvent = new ApiEvent();
                    testEvent.QueriedEntities = 'Account, Lead';
                    testEvent.RowsProcessed = 2001;
                    
                    // test that the Apex returns true for this event
                    LeadExportEventCondition  eventCondition = new LeadExportEventCondition();
                    System.assert(eventCondition.evaluate(testEvent));   
                }
               
               /**
                * Positive test case 2: If a ReportEvent has Lead as a queried entity and more than 2000 rows 
                * processed, then the evaluate method of our policy's Apex should return true.
                **/ 
                static testMethod void testReportEventPositiveTestCase() {
                    // set up our event and its field values
                    ReportEvent testEvent = new ReportEvent();
                    testEvent.QueriedEntities = 'Account, Lead';
                    testEvent.RowsProcessed = 2001;
                    
                    // test that the Apex returns true for this event
                    LeadExportEventCondition  eventCondition = new LeadExportEventCondition();
                    System.assert(eventCondition.evaluate(testEvent));   
                }
               
               /**
                * Positive test case 3: If a ListViewEvent has Lead as a queried entity and more than 2000 rows 
                * processed, then the evaluate method of our policy's Apex should return true.
                **/ 
                static testMethod void testListViewEventPositiveTestCase() {
                    // set up our event and its field values
                    ListViewEvent testEvent = new ListViewEvent();
                    testEvent.QueriedEntities = 'Account, Lead';
                    testEvent.RowsProcessed = 2001;
                    
                    // test that the Apex returns true for this event
                    LeadExportEventCondition  eventCondition = new LeadExportEventCondition();
                    System.assert(eventCondition.evaluate(testEvent));   
                }
               
               /**
                * Positive test case 4: If an event does not have Lead as a queried entity and has more 
                * than 2000 rows processed, then the evaluate method of our policy's Apex 
                * should return false.
                **/ 
                static testMethod void testOtherQueriedEntityPositiveTestCase() {
                    // set up our event and its field values
                    ApiEvent testEvent = new ApiEvent();
                    testEvent.QueriedEntities = 'Account';
                    testEvent.RowsProcessed = 2001;
                    
                    // test that the Apex returns false for this event
                    LeadExportEventCondition  eventCondition = new LeadExportEventCondition();
                    System.assertEquals(false, eventCondition.evaluate(testEvent));   
                }
                
              /**
                * Positive test case 5: If an event has Lead as a queried entity and does not have 
                * more than 2000 rows processed, then the evaluate method of our policy's Apex 
                * should return false.
                **/ 
                static testMethod void testFewerRowsProcessedPositiveTestCase() {
                    // set up our event and its field values
                    ReportEvent testEvent = new ReportEvent();
                    testEvent.QueriedEntities = 'Account, Lead';
                    testEvent.RowsProcessed = 2000;
                    
                    // test that the Apex returns false for this event
                    LeadExportEventCondition  eventCondition = new LeadExportEventCondition();
                    System.assertEquals(false, eventCondition.evaluate(testEvent));   
                }
                
              /**
                * Positive test case 6: If an event does not have Lead as a queried entity and does not have 
                * more than 2000 rows processed, then the evaluate method of our policy's Apex 
                * should return false.
                **/ 
                static testMethod void testNoConditionsMetPositiveTestCase() {
                    // set up our event and its field values
                    ListViewEvent testEvent = new ListViewEvent();
                    testEvent.QueriedEntities = 'Account';
                    testEvent.RowsProcessed = 2000;
                    
                    // test that the Apex returns false for this event
                    LeadExportEventCondition  eventCondition = new LeadExportEventCondition();
                    System.assertEquals(false, eventCondition.evaluate(testEvent));   
                }
                
                /**
                 * ------------ NEGATIVE TEST CASES ------------
                 **/
               
               /**
                * Negative test case 1: If an event is a type other than ApiEvent, ReportEvent, or ListViewEvent,
                * then the evaluate method of our policy's Apex should return false.
                **/
                static testMethod void testOtherEventObject() {
                    LoginEvent loginEvent = new LoginEvent();
                    LeadExportEventCondition  eventCondition = new LeadExportEventCondition();
                    System.assertEquals(false, eventCondition.evaluate(loginEvent));   
                } 
           
               /**
                * Negative test case 2: If an event is null, then the evaluate method of our policy's
                * Apex should return false.
                **/
                static testMethod void testNullEventObject() {
                    LeadExportEventCondition  eventCondition = new LeadExportEventCondition();
                    System.assertEquals(false, eventCondition.evaluate(null));   
                } 
               
               /**
                * Negative test case 3: If an event has a null QueriedEntities value, then the evaluate method 
                * of our policy's Apex should return false.
                **/
                static testMethod void testNullQueriedEntities() {
                    ApiEvent testEvent = new ApiEvent(); 
                    testEvent.QueriedEntities = null;
                    testEvent.RowsProcessed = 2001;
                    
                    LeadExportEventCondition  eventCondition = new LeadExportEventCondition();
                    System.assertEquals(false, eventCondition.evaluate(testEvent));   
                }
               
               /**
                * Negative test case 4: If an event has a null RowsProcessed value, then the evaluate method 
                * of our policy's Apex should return false.
                **/
                static testMethod void testNullRowsProcessed() {
                    ReportEvent testEvent = new ReportEvent(); 
                    testEvent.QueriedEntities = 'Account, Lead';
                    testEvent.RowsProcessed = null;
                    
                    LeadExportEventCondition  eventCondition = new LeadExportEventCondition();
                    System.assertEquals(false, eventCondition.evaluate(testEvent));   
                } 
           }

          Настройка кода политики после выполнения тестов

          Например, вы выполните тесты, а тестовый сценарий testNullQueriedEntities не выполнит System.NullPointerException: Attempt to de-reference a null object ошибки. Отличная новость, тесты определили область политики безопасности транзакций, которая не проверяет неожиданные или нулевые значения. Поскольку политики выполняются во время важных операций организации, обеспечьте наименее проблемный сбой политик, чтобы они не блокировали важные функции при возникновении ошибки.

          Ниже указан способ обновления метода evaluate в классе Apex для грациозной обработки нулевых значений.

          private boolean evaluate(String queriedEntities, Decimal rowsProcessed) {
              boolean containsLead = queriedEntities != null ? queriedEntities.contains('Lead') : false;
              if (containsLead && rowsProcessed > 2000){
                  return true;
              }
              return false;
          }

          Мы изменили код, чтобы перед выполнением операции .contains над переменной queriedEntities сначала проверить, является ли значение нулевым. Это изменение обеспечивает отсутствие обратной ссылки а нулевой объект.

          В общем, при наступлении неожиданных значений или ситуаций в коде Apex, у вас есть два варианта: Определите наиболее приемлемый вариант для ваших пользователей.

          • Пропустите значения или ситуацию и верните false, чтобы политика не сработала.
          • Ошибка закрытия операции посредством возврата true.

          Расширенный пример

          Ниже указана более сложная политика Apex, использующая запросы SOQL для получения профиля пользователя, пытающегося войти в систему.

          global class ProfileIdentityEventCondition implements TxnSecurity.EventCondition {
          
              // For these powerful profiles, let's prompt users to complete 2FA
              private Set<String> PROFILES_TO_MONITOR = new Set<String> { 
                  'System Administrator', 
                  'Custom Admin Profile'
              };
              
              public boolean evaluate(SObject event) {
                  LoginEvent loginEvent = (LoginEvent) event;
                  String userId = loginEvent.UserId;
                  
                  // get the Profile name from the current users profileId
                  Profile profile = [SELECT Name FROM Profile WHERE Id IN 
                              (SELECT profileId FROM User WHERE Id = :userId)];
                  
                  // check if the name of the Profile is one of the ones we want to monitor
                  if (PROFILES_TO_MONITOR.contains(profile.Name)) {
                      return true;
                  }
                  
                  return false;
              }   
           }

          Ниже указан наш план тестирования для положительных тестовых случаев:

            • Если у пользователя, пытающегося войти в систему, есть профиль, который мы хотим отслеживать, метод evaluate возвращает true.
            • Если пользователь, пытающийся войти в систему, не имеет нужного профиля, метод evaluate возвращает false.

          И вот наш план для отрицательных тестовых случаев:

            • Если запрос объекта «Профиль» возвращает исключение, метод evaluate возвращает false.
            • Если запрос объекта «Профиль» возвращает значение null, то метод evaluate возвращает значение false.

          Поскольку каждый пользователь Salesforce всегда имеет назначенный профиль, не необходимости создавать отрицательный тест. Также невозможно создать фактические тесты для двух отрицательных способов использования. Мы сами займемся ими при обновлении самой политики. Но мы четко указываем случаи использования в своем плане, чтобы обязательно охватить много разных ситуаций.

          Положительные способы тестирования зависят от результатов запросов SQQL. Чтобы обеспечить корректное выполнение тестирования, необходимо также создать данные тестов. Рассмотрим код тестирования.

          /**
           * Tests for the ProfileIdentityEventCondition class, to make sure that our 
           * Transaction Security Apex logic handles events and event field values as expected.
           **/
           @isTest
           public class ProfileIdentityEventConditionTest {
           
              /**
               * ------------ POSITIVE TEST CASES ------------
               ** /
           
               /**
                * Positive test case 1: Evaluate will return true when user has the "System 
                * Administrator" profile.
                **/ 
                static testMethod void testUserWithSysAdminProfile() {
                    // insert a User for our test which has the System Admin profile
                    Profile profile = [SELECT Id FROM Profile WHERE Name='System Administrator'];
                    assertOnProfile(profile.id, true); 
                }
          
               /**
                * Positive test case 2: Evaluate will return true when the user has the "Custom
                * Admin Profile"
                **/ 
                static testMethod void testUserWithCustomProfile() {
                    // insert a User for our test which has the System Admin profile
                    Profile profile = [SELECT Id FROM Profile WHERE Name='Custom Admin Profile'];
                    assertOnProfile(profile.id, true);
                }
                
               /**
                * Positive test case 3: Evalueate will return false when user doesn't have
                * a profile we're interested in. In this case we'll be using a profile called
                * 'Standard User'.
                **/ 
                static testMethod void testUserWithSomeProfile() {
                    // insert a User for our test which has the System Admin profile
                    Profile profile = [SELECT Id FROM Profile WHERE Name='Standard User'];
                    assertOnProfile(profile.id, false);
                }
                
                /**
                 * Helper to assert on different profiles.
                 **/
                static void assertOnProfile(String profileId, boolean expected){
                    User user = createUserWithProfile(profileId);
                    insert user;
                
                    // set up our event and its field values
                    LoginEvent testEvent = new LoginEvent();
                    testEvent.UserId = user.Id;
                    
                    // test that the Apex returns true for this event
                    ProfileIdentityEventCondition  eventCondition = new ProfileIdentityEventCondition();
                    System.assertEquals(expected, eventCondition.evaluate(testEvent));  
                }
                
                /**
                 * Helper to create a user with the given profileId.
                 **/
                static User createUserWithProfile(String profileId){
                    // Usernames have to be unique.
                    String username = 'ProfileIdentityEventCondition@Test.com';
                    
                    User user = new User(Alias = 'standt', Email='standarduser@testorg.com', 
                    EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US', 
                    LocaleSidKey='en_US', ProfileId = profileId, 
                    TimeZoneSidKey='America/Los_Angeles', UserName=username);
                    return user;
                }
           }

          Обработаем два отрицательных способа тестирования, обновив код политики безопасности транзакций для проверки наличия исключений или нулевых результатов при запросе объекта «Профиль».

          global class ProfileIdentityEventCondition implements TxnSecurity.EventCondition {
          
              // For these powerful profiles, let's prompt users to complete 2FA
              private Set<String> PROFILES_TO_MONITOR = new Set<String> { 
                  'System Administrator', 
                  'Custom Admin Profile'
              };
              
              public boolean evaluate(SObject event) {
                  try{
                      LoginEvent loginEvent = (LoginEvent) event;
                      String userId = loginEvent.UserId;
                      
                      // get the Profile name from the current users profileId
                      Profile profile = [SELECT Name FROM Profile WHERE Id IN 
                                  (SELECT profileId FROM User WHERE Id = :userId)];
                      
                      if (profile == null){
                          return false;
                      }
                      
                      // check if the name of the Profile is one of the ones we want to monitor
                      if (PROFILES_TO_MONITOR.contains(profile.Name)) {
                          return true;
                      }
                      return false;
                  } catch(Exception ex){
                      System.debug('Exception: ' + ex);
                      return false;   
                  }
              }   
           }
           
          Загрузка
          Salesforce Help | Article