Loading
Configuration et maintenance de votre organisation Salesforce
Table des matières
Sélectionner des filtres

          Aucun résultat
          Aucun résultat
          Voici quelques conseils de recherche

          Vérifiez l'orthographe de vos mots-clés.
          Utilisez des termes de recherche plus généraux.
          Sélectionnez moins de filtres pour élargir votre recherche.

          Recherchez dans toute l’aide de Salesforce
          Test Apex de la sécurité des transactions avancée

          Test Apex de la sécurité des transactions avancée

          L'écriture de tests robustes est une meilleure pratique d'ingénierie qui permet de vérifier que votre code se comporte normalement et détecte les erreurs avant vos utilisateurs et clients. Il est encore plus important d'écrire des tests pour le code Apex de votre stratégie de sécurité des transactions, car il est exécuté pendant les actions utilisateur critiques dans votre organisation Salesforce. Par exemple, un bogue dans votre stratégie LoginEvent qui n'est pas détecté pendant le test peut bloquer vos utilisateurs hors de votre organisation, situation qu'il est préférable d'éviter.

          Éditions requises

          Disponible avec Salesforce Classic (pas disponible dans toutes les organisations) et avec Lightning Experience

          Disponible avec : Enterprise Edition, Unlimited Edition et Developer Edition

          Nécessite des abonnements complémentaires Salesforce Shield ou Salesforce Event Monitoring.

          Avertissement
          Avertissement Utilisez l'API version 47.0 ou supérieure lors de l'écriture de tests Apex pour des stratégies de sécurité des transactions avancée.

          Lorsque vous testez votre code Apex en simulant une série de conditions, par définition vous écrivez des tests unitaires. L'écriture de tests unitaires ne suffit pas. Travaillez avec vos équipes de sécurité et commerciale pour comprendre tous vos cas d'utilisation. Créez ensuite un plan de test complet qui reproduit l'expérience de vos utilisateurs en utilisant des données test dans un environnement sandbox. Généralement, le plan de test inclut un test manuel et un test automatisé utilisant des outils externes tels que Selenium.

          Pour faciliter vos premiers pas, examinez ci-dessous quelques exemples de tests unitaires. Nous souhaitons tester la stratégie Apex suivante.

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

          Planification et écriture de tests

          Avant de commencer à écrire des tests, examinons les cas d'utilisation positifs et négatifs que notre plan de test couvre.

          Cas de tests positifs
          Si la méthode evaluate reçoit... et... Ensuite, la méthode evaluate renvoie...
          Un objet ApiEvent L'objet ApiEvent contient Lead dans son champ QueriedEntities et une valeur supérieure à 2000 dans son champ RowsProcessed true
          Un objet ReportEvent L'objet ReportEvent contient Lead dans son champ QueriedEntities et une valeur supérieure à 2000 dans son champ RowsProcessed true
          Un objet ListViewEvent L'objet ListViewEvent contient Lead dans son champ QueriedEntities et une valeur supérieure à 2000 dans son champ RowsProcessed true
          N'importe quel objet event L'objet event ne contient pas Lead dans son champ QueriedEntities et contient une valeur supérieure à 2000 dans son champ RowsProcessed false
          N'importe quel objet event L'objet event contient Lead dans son champ QueriedEntities et une valeur inférieure ou égale à 2000 dans son champ RowsProcessed false
          N'importe quel objet event L'objet event ne contient pas Lead dans son champ QueriedEntities et contient une valeur inférieure ou égale à 2000 dans son champ RowsProcessed false
          Cas de tests négatifs
          Si la méthode evaluate reçoit... et... Ensuite, la méthode evaluate renvoie...
          Un objet LoginEvent (sans condition) false
          Une valeur nulle (sans condition) false
          Un objet ApiEvent Le champ QueriedEntities est nul false
          Un objet ReportEvent Le champ RowsProcessed est nul false

          Voici le code du test Apex qui implémente tous ces cas d'utilisation.

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

          Affinage du code de la stratégie après l'exécution des tests

          Supposons que vous exécutez les tests et que la requête de test testNullQueriedEntities échoue avec l’erreur System.NullPointerException: Attempt to de-reference a null object. Bonne nouvelle, les tests ont identifié dans la stratégie de sécurité des transactions une zone qui ne contrôle pas les valeurs inattendues ou nulles. Les stratégies sont exécutées durant les opérations critiques de l'organisation. Par conséquent, assurez-vous qu'elles échouent en douceur en cas d'erreur, afin de ne pas bloquer les fonctionnalités importantes.

          Voici comment mettre à jour la méthode evaluate dans la classe Apex pour gérer ces valeurs null de façon appropriée.

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

          Nous avons modifié le code, afin de vérifier que la valeur est nul avant d'exécuter l'opération .contains dans la variable queriedEntities. Cette modification s'assure que le code ne déréférence pas un objet nul.

          Généralement, lorsque vous rencontrez des valeurs ou des situations inattendues dans votre code Apex, vous avez deux possibilités. Déterminez la meilleure solution pour vos utilisateurs en choisissant une option :

          • Ignorer les valeurs ou la situation et renvoyer false, afin de ne pas déclencher la stratégie.
          • Fermer et échouer l'opération en renvoyant true.

          Exemple avancé

          Voici une stratégie Apex plus complexe qui utilise des requêtes SOQL afin de récupérer le profil de l'utilisateur qui tente de se connecter.

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

          Voici notre plan pour des cas de tests positifs :

            • Si l'utilisateur qui tente de se connecter correspond au profil que vous souhaitez suivre, la méthode evaluate renvoie true.
            • Si l'utilisateur qui tente de se connecter ne correspond pas au profil que vous souhaitez suivre, la méthode evaluate renvoie false.

          Voici notre plan pour les cas de tests négatifs :

            • Si l'interrogation de l'objet Profil produit une exception, la méthode evaluate renvoie false.
            • Si l'interrogation de l'objet Profil renvoie null, la méthode evaluate renvoie false.

          Chaque utilisateur Salesforce est toujours associé à un profil. Par conséquent, il n'est pas nécessaire de créer un test négatif. De plus, il n'est pas possible de créer des tests réels pour les deux cas de test négatif. Nous nous en chargeons pendant la mise à jour de la stratégie. Cependant, nous répertorions explicitement les cas d'utilisation dans notre plan pour nous assurer de couvrir de nombreuses situations différentes.

          Les cas de test positif s'appuient sur les résultats de requêtes SQQL. Pour vous assurer que ces requêtes sont correctement exécutées, vous devez également créer quelques données test. Examinons le code de test.

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

          Nous allons gérer deux cas de test négatif en mettant à jour le code de la stratégie de sécurité des transactions afin de vérifier les exceptions ou les résultats null lors de l'interrogation de l'objet Profil.

          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;   
                  }
              }   
           }
           
          Chargement
          Salesforce Help | Article