Apex Best practices

General about Apex Best practices

The code styling is based on Google Java Docs.

  • When creating asynchronous apex, use Queueable methods over Future. Future methods don’t offer any advantages over queueable (see. Stackoverflow).
  • Initialize constants outside of the constructor (Developer Salesforce).
  • For HTTP callout, use named credentials.
  • Delete debug statements (link why?).
  • Organize your classes into folders, don’t put anything into the default folder.

Code styling

1. Set the right margin in your code editor to 120 characters. The right margin is the line on the right side that reminds you when you need to break the line. Setup in VS Code (link).

2. As tabulation, use 4 whitespaces. Make sure that you use actual whitespaces and not tabulation sign.

3. Use descriptive names.

List cnts = new List(); // wrong 
List contacts = new List(); // correct 
for(Account a : accounts) {...} // wrong 
for(Account account : accounts) {...} // correct

4. Always use curly brackets, even if you have a one-line-statement.

if(true)
    return 'Igor'; // wrong

if(true) {
    return 'Igor'; // correct
}

5. With long Boolean statements refactor them into a separate Boolean variable. Example:

Boolean isRefund = chargeEventData.object_x == REFUND_OBJECT_NAME;

Boolean chargeHasRefunds = chargeEventData.refunds != null;

if(isRefund || chargeHasRefunds) {
…
}

Class naming

  • If you have a class called “MyClass”, name the test class “MyClassTest”.
  • All classes should be named with camelCase (see Google Docs). Actually, everything except constants should be in camelCase.
  • “Controller” is only allowed in the class name if it’s actually a controller for Aura/LWC/Visualforce.
  • “Handler” is only allowed in the class name if it’s an actual handler, which is quite rare. Handlers are usually used for integrations to handle the JSON response/request or in the trigger framework.

Methods

1. If the method exceeds the 120-character limit, align arguments to be on the same level.

public static Map createSuperLongMap(String longArgumentName1, String longArgumentName2, String longArgumentName3) { 
        // Example of too long method name 
        return null; 
} 

public static Map createSuperLongMap( 
    String longArgumentName1,
    String longArgumentName2, 
    String longArgumentName3 
) { 
        // Example of correct alignment 
        return null; 
}

2. Don’t use “get…” unless it’s an actual getter method.

3. In general, try to keep your methods within 10-20 lines. You can write dirty code with one method of 1000 lines to verify that your solution is working. But after that, make sure to refactor your method into smaller methods.

4. Align methods in the order they are called from the main process.

Variables

1. Don’t use tabs for alignment of variables. Always use one whitespace.

Opportunity opportunity = new Opportunity();
String      name        = 'Igor'; // wrong

String name = 'Igor'; // correct

2. Don’t duplicate “list” or “map” into the variable name, it’s clear from the data type.

List<String> accountNamesList; // Wrong
List<String> accountNames; // Correct

SOQL

1. Use the following syntax

List<Opportunity> opportunity= [ 
    SELECT Id, Name, Amount 
    FROM Opportunity 
    WHERE Id IN :idSet 
];

2. If the SOQL fits into the 120-character limit, write it in one line.

3. Refactor your SOQL calls into a separate method. In the example above:

public void method(Set idSet) { 
  List opportunity = queryOpportunitiesByIds(idSet);  
} 

public List queryOpportunitiesByIds(Set idSet) { 
    return [ 
       SELECT Id, Name, Amount 
       FROM Opportunity 
       WHERE Id 
       IN :idSet 
  ]; 
}

4. Depending on the complexity of your code and the number of SOQL queries you expect, you should create a persistence class and move your SOQL queries there.

public void method(Set idSet) { 
  List opportunity = OppoertunityQueries.queryOpportunitiesByIds(idSet); 
}

5. Persistence class:

public class OpportunityQueries { 
  public static List queryOpportunitiesByIds(Set idSet) { 
    return [ 
       SELECT Id, Name, Amount 
       FROM Opportunity 
       WHERE Id 
       IN :idSet 
    ]; 
  } 
}

Code documentation

1. Don’t comment inside of the method, move your comments to the method description. Example:

/** 
* This method is a part of Opportunity calculation process. The method is called * from scheduled class "OpportunityScheduled". 
* 
* @param idSet   Ids of opportunities that are closed in the last month 
*                from scheduled class. Never null. 
*/ 
public void method(Set idSet) { 
  List opportunity = queryOpportunitiesByIds(idSet); 
  ... 
}

2. In the method descriptions write:

  • Where is the method used?
  • What are the edge cases?
  • How is this method related to other processes?
  • If there was a bug: The reason for the bug and the fix

3. Don’t write:

  • What a method is doing. We can read from the code.
  • Changes and dates of changes. That’s the job of Git.

Advanced best practices

  • Don’t return null (Code by Amir).
  • Don’t abuse if…else… (Dev To).
  • Return early pattern (Dev To).
    a. But at the same time: avoid multiple returns if possible. It is much simpler to
    debug the code if there is only one return.
  • Use trigger framework.

Tests Best Practices in Apex

General Tips

Tests are a crucial aspect for a team of developers for several reasons. They help
ensure the stability, reliability, and maintainability of the codebase as it evolves. Here
are some tips to follow when writing tests:

  • To deploy to production, at least 75% code coverage is required, but aiming for 90%
    or higher is recommended. The focus should not be on the percentage of code
    coverage but on ensuring that every use case is covered. Also, note that you must
    have at least one line of test coverage for triggers.
  • Never use seeAllData = true. Note that certain objects such as User, Profile, and
    Organization can be accessed without setting seeAllData = true.
  • Name your test classes based on the class that you are testing. For example,
    MyClass should have a corresponding test class named MyClassTest .
  • Test classes can be either private or public. If you’re using a test class for unit
    testing only, declare it as private. Public test classes are typically used for test data
    factory classes.
  • If you frequently have to run multiple tests, organize them into test suites.
  • Use lists to bulk insert/update your records. Even though these are test classes, we
    still want to optimize for the run time.
  • If your logic is around created dates of the records, use Test.setCreatedDate(recordId,
    createdDatetime)
    .

Testing Scenarios

Some possible scenarios to test in Apex include:

  • If your class throws an exception, write a separate test for the case where you
    trigger this exception and catch it.
  • You should have at least one test method for each public method in the class.
  • If one of the methods is private and a vital part of your system, use @TestVisible
    annotation on the method.
  • Single records: Test to verify that a single record produces the correct, expected
    result.
  • Bulk records: Test that Apex code, whether a trigger, a class, or an extension, works
    correctly with 2 to 200 records.
  • Positive scenarios: Test that the system can save a record without error.
  • Negative scenarios: Test that the system returns an error when expected.
  • Restricted user: Test that a user with restricted access to the objects used in the
    code sees the expected behavior, such as being able to run the code or receiving
    error messages.

Write a test class

You can use the following template to create a test class:

@IsTest
public with sharing class MyClassTest {

   @TestSetup
   static void setup() {
   }

   @IsTest
   static void execute_UnitTest() {
       Test.startTest();
       Test.stopTest();
   }
}
  • Test classes and methods must be annotated with the @IsTest, not with the testMethod keyword.
  • Test methods should only be located within @IsTest annotated classes.
  • Use the @testSetup annotation to create test records once in a method. The records that are inserted in the setup are persistent throughout the whole execution. When you change one of the objects, all the changes are rolled back after each test method.
  • Use the Test.startTest() and Test.stopTest() methods to manage governor limits in your tests. You should also test for governor limits using the Limits class.
  • Avoid using hard-coded IDs in test classes or any Apex classes since they do not persist through Sandboxes, and your tests will fail once deployed to another environment.
  • There are certain data types that you can access without inserting them, such as metadata types.
  • Use the Assert class with meaningful assert names. Where possible, specify a message in the assert method.
  • Don’t use old System.assertEquals method.
  • As Apex runs in system mode, record sharing and permissions are not taken into account. Use System.runAs() to enforce record sharing in your tests.

Test Data Factory

The main idea behind a test data factory is to create a single class that you can use throughout all your tests. Each method in the data factory should return an object of a specific type (e.g. Account).

  • Avoid naming your data factory methods with “get…” as the “get” name is reserved for actual getters in your classes.
  • Create a TestFactory class with the @isTest annotation to exclude it from the organization’s code size limit.
  • Avoid inserting records inside the factory methods, or at least build in the logic to avoid insertion.

Mocking in Apex Tests

If you have a callout in the system, you will need to use a mock class for testing. Salesforce has a good article in its documentation. For example, let’s say you have an integration that calls an API and receives a response that you handle in your Apex code. How would you get a sample of the JSON response into your test class? From my experience, there are two ways of doing it:

  • Hard-code your JSON as a string into your mock class. This approach works if the string is relatively small. If you need to test a large JSON response, it’s better to use static resources.
  • Upload a JSON file as a static resource and query the file content in the mock class. This approach is more flexible, but APIs don’t change very often. After you get the content of the file, you can modify the string to cover different test cases.

Testing Emails in Apex

Testing how your email functionality behaves in Apex is not fully possible. For example, let’s say you have a trigger that sends emails to clients. In the test classes, you can only test that some emails have been sent. However, you cannot verify if these emails were sent to the right people, what was the content of these emails, or if these emails were actually delivered. We cannot write tests to answer these questions.
Another thing to consider is that you should avoid writing a very generic test case for the business process that includes email sending from Apex. For example, if you send an email from an opportunity trigger, in your test method you test that when you updated five opportunities, five emails are sent to clients. After some time, you add another business process where you send an additional email to the opportunity owner that clients have been notified. Suddenly, your old test cases break because now you send six emails instead of five. I’ve seen test cases where it’s nearly impossible to guess how
many emails will be sent.

Testing Batch Apex

Testing batch Apex classes is a straightforward process, which you can read about in the Use batch Trailhead. However, it is important to use the Database.executeBatch() method to test how all three batch methods work together. If you only test each method separately, you will miss this important scenario.

Testing Scheduled Apex

Quite often, you will write a combination of a scheduled class and a batch class. Or it can be any other class, but usually, you will have a separate class that contains the main business logic. In this scenario, you should test your business logic in the class that actually executes this logic. In the scheduled class, you can just test that your class is scheduled successfully.

Visualforce Best Practices

When should I NOT use VF for PDF generation?

  • When a client wants any fonts besides the standard VF PDF fonts.
  • When the client wants to have dynamic right-to-left written languages with any fonts (e.g. Hebrew or Arabic).
  • When the PDF is design-heavy and can’t be done with a background image.

Multi-Language Support

Displaying non-Latin-based languages can be problematic in VF PDF. Most of these
languages are only supported with “Arial Unicode MS” font. This means you can’t
make it bold or apply any styles to the text. You can imagine how awful the standard
font looks like.
As a workaround, if you only need static texts, you can use an image of the text you
need. It works well, for example, for a header row in a table.

I need an insanely good PDF design

Sometimes, the client will show an insanely good-looking PDF with a lot of complex
elements and ask if you can design the same with VF. For these cases, it’s better to use
something else than VF, but there is one workaround possible – a background image.
You can upload the image as a background and write all dynamic values over it. This
way, you will have any design you want in the image and still be very dynamic with the
data you display.
First, upload your image as a static resource. After that, you can use it in your CSS:

@page {
    background-image: url("{!$Resource.YourImageName}");
    background-repeat: no-repeat !important;
    background-position: center center !important;
    background-size: 100% 100% !important;
}

Page breaks

Sometimes we want to control the page break. Use age-break-before, page-break-after,

and page-break-inside (link). Let’s say you display a big table with products over

multiple PDF pages. Here is how page breaks can help you:

  • Restrict the number of products displayed per page.
  • Adjust the number of products per page based on a field on a record.
  • Make sure the table is never cut in the middle of the row.

Show table header on each page

table {
    -fs-table-paginate: paginate;
}

Footer/Header

Salesforce short article is pretty good: Link
In addition to that, you use “:first”, “:last” in combination with footer or header if you
need to display a header only on the last page.

@page :first {
    @top-center {
        content: element(header);
    }
}

Alternate row color

One of the simplest ways to make your PDF design better is to alternate the row color.
For example, making every second row in the table slightly green. We can use a
“counter” variable to count how many rows in <apex:repeat…>tag we’ve used.
If we know the maximum amount of products per page, we can spot when the counter
exceeds this value and ascend it back to the default value.

<apex:page renderAs="pdf">
    <apex:variable value="{!1}" var="counter" />
    <apex:repeat value="{!products}" var="product">
      <tr style="background-color:{!IF(mod(counter,2)==0,'#e2f3f3','#ffffff')};">
          ...
      </tr>
    </apex:repeat>
    <apex:variable value="{!IF(counter == 5, 1, counter+1)}" var="counter" />
<apex:page>
<h4 class="item-title">admin</h4>

admin

Related Posts

blog images 1

Salesforce udemy courses

Leave a Reply

Your email address will not be published. Required fields are marked *

t.me/ihor_igor

Subscribe for news about new courses or discounts.

No spam, we promise.


    © 2023 All Rights Reserved by site  igor@cloud-prism.com