Section 1 – Triggers, Testing, and Security
In this section, we will begin by reviewing common mistakes made by developers in their Apex code and how to avoid them. This will lead us into a discussion around debugging Apex when we find an error. Following this, we will discuss how to work with Salesforce triggers effectively and how to improve our Apex testing, as well as understand how to improve security within our code.
This section covers the following chapters:
- Chapter 1, Common Apex Mistakes
- Chapter 2, Debugging Apex
- Chapter 3, Triggers and Managing Trigger Execution
- Chapter 4, Exceptions and Exception Handling
- Chapter 5, Testing Apex Code
- Chapter 6, Secure Apex Programming
Chapter 1: Common Apex Mistakes
In this chapter, we will cover common mistakes made when developing in Apex and the appropriate defensive programming techniques to avoid them. Before we begin to move into more advanced topics within the Apex language, it is important that we first of all ensure a common grounding in removing any common errors within our code. For some of you, this material may simply be a refresher or a reiteration of some practices; however, these mistakes form the basis of many of the common exceptions and errors that I have seen in my time working with the Salesforce platform. By the end of this chapter, you will hopefully be more aware of when these mistakes may occur within your code and be able to develop in a defensive style to avoid them.
In this chapter, we will cover the following topics:
- Null pointer exceptions
- Bulkification of Apex code to avoid Governor Limits
- Hardcoding references to specific object instances
- Patterns to deal with managing data across a transaction
Technical Requirements
The code for the chapter can be found here, https://github.com/PacktPublishing/Mastering-Apex-Programming/tree/Chapter-1.
Null pointer exceptions
Almost every developer working with the Salesforce platform has encountered the dreaded phrase Attempt to de-reference a null object. At its heart, this is one of the simplest errors to both generate and handle effectively, but its error message can cause great confusion for new and experienced developers alike, as it is often unclear how the exception is occurring.
Let's start by discussing in the abstract form how the error is generated. This is definitively a runtime error, caused by the system attempting to read data from memory where the memory is blank. Apex is built on top of the Java language and uses the Java Virtual Machine (JVM) runtime under the hood. What follows is a highly simplified discussion of how Java manages memory, which will help us to understand what is happening under the hood.
Whenever an object is instantiated in Java, it is created and managed on the heap, which is a block of memory used to dynamically hold data for objects and classes at runtime. A separate set of memory, called the stack, stores references to these objects and instances. So, in simplistic terms, when you instantiate an instance called paul of a Person class, that instance is stored on the heap and a reference to this heap memory is stored on the stack, with the label paul. Apex is built on Java and compiles down to Java bytecode (this started after an update from Salesforce in 2012), and, although Apex does not utilize a full version of the JVM, it uses the JVM as the basis for its operations, including garbage and memory management.
With this in mind, we are now better able to understand how the two most common types of NullPointerException within Apex occur: when working with specific object instances and when referencing values on maps.
Exceptions on object instances
Let's imagine I have the following code within my environment:
public class Person {
public String name;
}
Person paul;
In this code, we have a Person class defined, with a single publicly accessible member variable. We have then declared a variable, paul, using this new data type. In memory, Salesforce now has a label on the stack called paul that is not pointing to any address on the heap, as paul currently has the value of null.
If I now attempt to run System.debug(paul.name);, we will get a NullPointerException exception with the message Attempt to de-reference a null object. What is happening is that the system is trying to use the paul variable to retrieve the object instance and then access the name property of that instance. Because the instance is null, the reference to this memory does not exist, and so NullPointerException is thrown; that is, we have nothing to point with.
With this understanding of how memory management is working under the hood (in an approximate fashion) and how we are generating these errors, it is therefore pretty easy to see how we code against them—avoid calling methods and accessing variables and properties on an object that has not been instantiated. This can be done by ensuring we always call a constructor when initializing a variable, as shown in the following code snippet:
Person paul = new Person();
In general, when developing, we should pay atte...