Executing your code

After creating test data and calling test.startTest(), we are now left with executing our code. This is where our general testing pattern branches into three subpatterns. Each of these subpatterns does execute the code, but each in their own unique and important way. Of the three, the first subpattern is the easiest to understand and what you're most likely to have run across. I call this types of tests Positive tests because they're based in the positive assumption that your code is working as intended with valid data. The second subpattern is less intuitive, but far more powerful. I call these Negative tests because they test what happens when exceptions occur. Lastly, there are Permissions tests that test how your code functions with different users, roles, and permission sets. Let's take a more detailed look at all three.

Positive tests

Positive tests prove expected behavior. That is, they prove that the code functions as it is intended to when given valid input. Succinctly stated, this subpattern states that when given proper inputs, expected outputs are received. Care should be taken to evaluate all valid paths through the code. If your code is structured with conditional logic statements, ensure that you positively test each and every viable path through your code. Let's take a look at some class code and it's test to illustrate this:

Public class exampleCode {
  Public Integer add(Integer one, Integer two){
    return one + two;
  }
}

@isTest
private class exampleCode_Tests {
  @isTest static void test_Add_Postive() {
    exampleCode drWho = new exampleCode();
    Test.startTest();
    Integer testValue = drWho.add(5,7);
    Test.stopTest();
     System.assertEquals(12, testValue, 
       'Expected 5+7 to equal 12');
}

Our simple add() method accepts two integer arguments and returns a single integer in response. Our test follows our basic test pattern, but the simple nature of this method means we don't need to create our own test data here; we can just pass in two integers. You'll note, however, that we're creating the ExampleCode object before we start the test. Because this is a positive test, we'll provide the add method with two valid integers, in this case, 5 and 7. Because we're setting the input parameters, after a couple cups of coffee, we can do the math ourselves and assert that the value returned is 12. This is an extremely simple but clear example of how positive tests work.

Negative tests

Negative tests are less intuitive and require some additional setup. In return, negative tests prove that our code safely handles errors and exceptions. Because we're intentionally providing inputs to the code that will cause an exception to occur, we need to find a way to safely capture the exception without failing the test. Here, the subpattern works by setting a Boolean variable, say didPass to false in the setup of the test, and executing our to-be-tested code inside a Try/catch block. When our inputs cause the test to throw the exception, our test captures that in the catch block. If the type of exception and messaging we catch matches what we expect, we can set didPass to true. Outside of the Try/catch block and after the stopTest() call, we can assert that our didPass variable is true. That's a lot to take in, so let's look at the following code example:

Public class exampleCode {
  Public class exampleCodeException{}
  Public Static Integer division(Integer one, Integer two){
    if(two == 0) {
      Throw new exampleCodeException('Dividing by zero makes kittens cry');
    } 
    return one / two;
  }
}

private class exampleCode_Tests {
  @isTest static void test_Divide_Negative() {
    Boolean didCatchProperException = false;
    Test.startTest();
    Try {
      exampleCode.divide(1, 0);
    } catch (exampleCode.exampleCodeException AwesomeException){
      didCatchProperException = true;
    }
    Test.stopTest();
    System.assert(didCatchProperException,
    'Properly caught custom Exception');
}

We've added a division method to our ExampleCode class, and astute readers will notice the addition of a new custom exception class, exampleCodeException. In our test, we follow the same pattern as earlier of starting the test and executing our code. However, in this situation, we wrap the code execution in a Try/catch block. We're calling our divide method with an intentionally bad second integer, 0. Our divide method is on the lookout, however, for the second parameter being 0. Instead of letting the system throw a divide by zero error, our code throws an ExampleCodeException method. It's this exampleCodeException method that we're trapping in our test's catch block. This allows us to be certain that not just any exception is caught here. If, for instance, a divide by zero exception somehow still occurred, that exception would not be caught and the test would fail. In the end, we set didCatchProperException, our previously defined test Boolean to true so that we can assert that we did indeed catch the proper kind of exception.

Negative tests are considerably more powerful in the long run, helping ensure modifications still properly handle known exception situations properly. This is especially helpful when modifying the implementation of class methods. Additionally, because they're testing how the code reacts to invalid data, they are the de facto way of testing situations, such as invalid user entered data, fields that legitimately have apostrophes in them, and responses to third-party external web services that your code uses. Does your code properly handle the last name O'Connel? How about a fellow developer accidently linking a task by WhatId instead of WhoId? And how does your exchange rate API code handle the sudden lack of Internet connection due to severe weather? Negative tests ensure that you can answer these questions. More importantly, negative tests prove that you've defensively developed against at least those failure scenarios that were identified.

Permissions-based tests

Permissions-based testing ensures that your sharing and security model works as you expect it to. Sharing rules and security options are some of the most complicated aspects of Salesforce1 platform development. These are, therefore, some of the most important tests you can write. But wait you say! Isn't writing tests of the security and permissions model just a form of testing native platform functionality? Kind of. Unless otherwise specified, Apex code runs in a system context. In essence, this means that Apex code normally ignores the users' permissions and sharing rules. These tests help us ensure that the permissions and sharing rules are honored by our Apex code.

Effectively, permission tests follow the same general pattern as other tests with one small twist. When we go to execute the code, we'll execute that code with a different user. We're not just going to pick a user at random either. The user we run the test with becomes a crucial data point that we create. To actually run the test with our newly created user, we call the Salesforce1 platform's System.runAs() method. The RunAs method accepts a single user parameter and a block. Anything in that block of code is executed as with the profile, role, and permission sets of the user specified. Let's look at a basic use case for runAs():

private class exampleCode_Tests {
  @isTest static void test_getBankAccount_AsUserWithTimeLord() {
 User u = UserTestFactory.getUserWithProfile('TimeLord');
 System.runAs(u){
      Test.startTest();
       // This is executed as our user with the Timelord
      // profile
      Test.stopTest();
    }
    // Assertions
}

In this test method, we create a user with a call to our UserTestFactory. This user is set up with a given profile, in this case, the profile TimeLord. This sets us up to test our code as TimeLord. Now, when we execute our code, we can ensure that this profile's permissions are honored.

With this subpattern, we can test not only profiles, but also sharing rules and permission sets. Perhaps more importantly, we can test them both positively and negatively! Let's look at some examples. First, let's update our ExampleCode class with a method we want to permission test:

Public class exampleCode {
  Public class exampleCodeException{}
  Public Integer getBankAccount(Account a){
    // SuperSekr3tBankAccountNum__c is an encrypted field
    a = [SELECT superSekr3tBankAccountNum__c 
FROM Account 
WHERE ID :a.id];
If(String.ValueOf(a.superSekr3tBankAccountNum__c).contains('*')) {
  Throw new exampleCodeException('Nope!');
}
     return a.SuperSekr3tBankAccountNum__c;
  }
}

Here's a positive test:

private class exampleCode_Tests {
  @isTest static void test_getBankAccount_Positive() {
    exampleCode drWho = new exampleCode();
    User u = UserTestFactory.getUserWithProfile('TimeLord);
    Account a = (Account)TestFactory.createSObject(new Account());
    Integer result;
    System.runAs(u){
      Test.startTest();
        result = drWho.getBankAccount(a);    
      Test.stopTest();
    }
    System.assertNotEquals(result, null,
      'Expected The Doctor to have access to bank #');
}

In this test, we expect a user with the TimeLord profile to be able to access the encrypted bank account number field. On the other hand, we want to ensure that other profiles do not have access. With this in mind, we can write a negative test that looks like this:

@isTest
private class exampleCode_Tests {
  @isTest static void test_getBankAccount_UberForNope() {
    exampleCode Dalek = new exampleCode();
    User u = UserTestFactory.getUserWithProfile('Dalek');
    Account a = (Account)TestFactory.createSObject(new Account());
    Boolean didCatchException = false;
   Integer result;
    System.runAs(u){
      Test.startTest();
      Try {
          result = Dalek.getBankAccount(a);
      } catch(exampleCode.ExampleCodeException e){
       if(e.getMessage().containsIgnoreCase('nope')){
         didCatchException = true;
      }
      Test.stopTest();
    }
    System.assert(didCatchException, 'Expected Daleks to be blocked');
}

Our getBankAccount() method queries for an encrypted field; if the user doesn't have permission to view it, it will return a masked value. If we detect that masked value, we throw an exception. Like our positive test, this test still requires a custom user with a given profile, but in this case we expect an exception.

Importantly, we're not limited to testing users with different profiles. We can, and should create tests for Apex-based sharing rules, roles, and permission sets. Permission sets are an incredibly powerful and fine-grained tool for extending permissions on a per-user basis, beyond what a user has from their profile. With permission sets, we can, for instance, establish a singular profile for the entire support staff and grant additional privileges to support managers. If you're not already a fan of permission sets, check them out, you soon will be! Testing permission sets requires just a few extra lines of code to test. This is a prime example, however, of where a custom test factory becomes an invaluable tool. Let's look at some code as another example of testing permission sets:

@isTest
private class exampleCode_Tests {
  @isTest static void test_getBankAccount_W_PermSet() {
    exampleCode ClaraOswald = new exampleCode();
    User u = UserTestFactory.getUserWithProfileAndPermSets('Standard User', new List<String>{'companion'});
    Account a = (Account)TestFactory.createSObject(new Account());
    Boolean result;
    System.runAs(u){
      Test.startTest();
        result = ClaraOswald.getBankAccount(a);    
      Test.stopTest();
    }
    System.assertNotEquals(result, null,
  'Expected ClaraOswald who has Companion Permissions to have access to the bank account');
}

As you can see from the code, our test is nearly identical to our positive profile permission test. In fact, the only difference in this test is the custom userTestFactory method we called. Because positive permission tests are so similar, you can often group them together in a data structure. This allows you to write a metatest that iterates over your data structure to test various permissions. This greatly simplifies your testing, allowing you to add an element to your data structure, instead of adding entirely new tests whenever a new permission set or profile is created. Here's how one such meta-test works:

@isTest
Public Class accountPermTests {

  public class PermissionTestData {
    Boolean isProfileTest {get;set;}
    Boolean isPermSetTest {get;set;}
    Boolean isPositiveTest {get;set;}
    String profileName {get;set;}
    String permSetName {get;set;}
    String exceptionTypeName {get;set;}
    String exceptionMessage {get;set;}
    String friendlyMessage {get;set;}
    String assertEqualsValue {get;set;}

    Public PermissionTestData(Boolean iisProfileTest,
Boolean iisPermSetTest, Boolean iisPositiveTest,
String iProfileName, String isPermSetName,
String iExceptionTypeName, String iExceptionMessage, String iFriendlyMessage iAssertEqualsValue) {
      this.isProfileTest = iisPositiveTest;
      this.isPermSetTest = iisPermSetTest;
      this.isPositiveTest = iisPositiveTest;
      this.profileName = iProfileName;
      this.permSetName = isPermSetName;
      this.exceptionTypeName = iExceptionTypeName;
      this.exceptionMessage = iExceptionMessage;
      this.friendlyMessage = iFriendlyMessage;
      this.assertEqualsValue = iAssertEqualsValue;
    }
  }

  private List<PermissionTestData> PTD = new List<PermissionTestData>();

  private List<PermissionTestData> setPopulatedTestData() {
    PTD.add(new PermissionTestData(true, false, true,
          'support', '', '', '',
          'Expected this test to pass'));
    PTD.add(new PermissionTestData(true, true, true, 'support',
          'Support Manager', 'ExampleCodeException',
          'No access for you',
          'Did not expect this test to pass as the permission set involved should not pass!'));
  }

  @isTest static void test_getBankAccount_WithPermSets() {
    for(PermissionTestData p: setPopulatedTestData()) {
      exampleCode instance = new ExampleCode();
      User u;
      Boolean didCatchException;
      Integer result;
      Account a = (Account)TestFactory.createSObject(new Account());
      if(p.isPermSetTest && p.isProfileTest
        && p.profileName != '' && p.permSetName != ''){
        u = UserTestFactory.getUserWithProfileAndPermSets (p.profileName, new List<String>{p.permSetName});
      } else if (p.isProfileTest && p.profileName != ''){
        u = UserTestFactory.getUserWithProfile(p.profileName);
      }
      Test.startTest();
      System.runAs(u){
        if(p.isPositiveTest) {
          result = instance.getBankAccount(a);
        } else {
          try {
            result = instance.getBankAccount(a);
          } catch(Exception e) {
            if(e.getTypeName() == p.exceptionTypeName &&
               e.getMessage().containsIgnoreCase(p.exceptionMessage)){
              didCatchException = true;
            }
          }
        }
      }
      Test.stopTest();

      if(p.isPositiveTest){
        System.AssertEquals(p.AssertEquals, Result, p.friendlyMessage);
      } else {
        System.assert(didCatchException, p.friendlyMessage);
      }
    }
}

A test setup like this has some upfront costs, namely writing the inner class data structure and thinking through what commonalities exist across your permissions-based tests. In the end though, it's much easier to maintain such a suite of tests, as you can add or modify the .add calls in setPermissionTestData much faster than writing a net-new test.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.104.153