logo
shape

Apex Tests

The Apex testing framework enables you to write and execute tests for your Apex classes and triggers on the Lightning Platform. They are used to validate the functionality of custom Apex code and ensure that the code meets the requirements and standards set by the organization.

Testing Apex
Verify Date

public class VerifyDate {
	
	//method to handle potential checks against two dates
	public static Date CheckDates(Date date1, Date date2) {
		//if date2 is within the next 30 days of date1, use date2.  Otherwise use the end of the month
		if(DateWithin30Days(date1,date2)) {
			return date2;
		} else {
			return SetEndOfMonthDate(date1);
		}
	}
	
	//method to check if date2 is within the next 30 days of date1
	private static Boolean DateWithin30Days(Date date1, Date date2) {
		//check for date2 being in the past
        	if( date2 < date1) { return false; }
        
        	//check that date2 is within (>=) 30 days of date1
        	Date date30Days = date1.addDays(30); //create a date 30 days away from date1
		if( date2 >= date30Days ) { return false; }
		else { return true; }
	}

	//method to return the end of the month of a given date
	private static Date SetEndOfMonthDate(Date date1) {
		Integer totalDays = Date.daysInMonth(date1.year(), date1.month());
		Date lastDay = Date.newInstance(date1.year(), date1.month(), totalDays);
		return lastDay;
	}

}
                    
Testing Verify Date
Here is an example of how you can write the test class named TestVerifyDate for the VerifyDate class. In order to cover all paths and achieve 100% code coverage, you need to cover both the cases where date2 is within 30 days of date1 and when it is not.

@isTest
public class TestVerifyDate {
    // test when date2 is within 30 days of date1
    @isTest
    static void testDateWithin30Days() {
        Date date1 = Date.today();
        Date date2 = Date.today().addDays(10); // within 30 days

        Date result = VerifyDate.CheckDates(date1, date2);
        System.assertEquals(date2, result, 'Date2 is within 30 days of Date1 so it should be returned');
    }

    // test when date2 is not within 30 days of date1
    @isTest
    static void testDateNotWithin30Days() {
        Date date1 = Date.today();
        Date date2 = Date.today().addDays(35); // more than 30 days

        Date result = VerifyDate.CheckDates(date1, date2);

        Integer totalDays = Date.daysInMonth(date1.year(), date1.month());
        Date lastDayOfMonth = Date.newInstance(date1.year(), date1.month(), totalDays);

        System.assertEquals(lastDayOfMonth, result, 'Date2 is not within 30 days of Date1 so end of the month should be returned');
    }
    
    // test when date2 is before date1
    @isTest
    static void testDate2BeforeDate1() {
        Date date1 = Date.today();
        Date date2 = Date.today().addDays(-5); // before date1

        Date result = VerifyDate.CheckDates(date1, date2);

        Integer totalDays = Date.daysInMonth(date1.year(), date1.month());
        Date lastDayOfMonth = Date.newInstance(date1.year(), date1.month(), totalDays);

        System.assertEquals(lastDayOfMonth, result, 'Date2 is before Date1 so end of the month should be returned');
    }
}

                    

In this test class, we have three methods. The first method tests the condition where date2 is within 30 days of date1. The second method tests the condition where date2 is more than 30 days away from date1, and the third method tests the condition where date2 is before date1. The System.assertEquals method is used to assert that the actual result matches the expected result. After writing your tests, be sure to run them and check the code coverage in the Developer Console or your Salesforce IDE to make sure you have achieved 100% coverage.
Simple Apex Trigger and Unit Test

//Create and install a simple Apex trigger which blocks inserts and updates to any contact with a last name of “INVALIDNAME”. 
//The trigger should add an error message to the contact record and prevent the record from being saved.

trigger RestrictContactByName on Contact (before insert, before update) {
	
	//check contacts prior to insert or update for invalid data
	For (Contact c : Trigger.New) {
		if(c.LastName == 'INVALIDNAME') {	//invalidname is invalid
			c.AddError('The Last Name "'+c.LastName+'" is not allowed for DML');
		}

	}

}
        
Here is a sample Unit Test for the above trigger to achieve 100% code coverage that includes both positive and negative test cases:

@isTest
public class TestRestrictContactByName {
    @isTest static void testInvalidName() {
        // Arrange
        Contact c = new Contact(LastName = 'INVALIDNAME', FirstName = 'Test');

        // Act
        Test.startTest();
        Database.SaveResult sr = Database.insert(c, false);
        Test.stopTest();

        // Assert
        System.assert(!sr.isSuccess(), 'Record should have failed to insert');
        System.assertEquals('The Last Name "INVALIDNAME" is not allowed for DML', sr.getErrors()[0].getMessage(), 'Error message should match');
    }
    
    @isTest static void testValidName() {
        // Arrange
        Contact c = new Contact(LastName = 'VALIDNAME', FirstName = 'Test');

        // Act
        Test.startTest();
        Database.SaveResult sr = Database.insert(c, false);
        Test.stopTest();

        // Assert
        System.assert(sr.isSuccess(), 'Record should have inserted successfully');
    }
}

        
Create a Contact Test Factory

//Create an Apex class that returns a list of contacts based on two incoming parameters:
//the number of contacts to generate and the last name for those contacts.
//Do not insert the contacts into the database.
//Create an Apex class in the public scope named RandomContactFactory without the @isTest annotation.
//Use a Public Static Method to consistently generate contacts with unique first names based on the iterated number in the format Test 1, Test 2, Test 3, etc.
//Method Name: generateRandomContacts without the @isTest annotation
//Parameter 1: Integer numContacts being the number of contacts to generate with unique first names
//Parameter 2: String lastName being the last name for those contacts
//Return Type: List

public class RandomContactFactory {
    public static List generateRandomContacts(Integer numContacts, String lastName) {
        List contacts = new List();
        for (Integer i = 1; i <= numContacts; i++) {
            Contact c = new Contact();
            c.FirstName = 'Test ' + i;
            c.LastName = lastName;
            contacts.add(c);
        }
        return contacts;
    }
}
        
Most Asked Questions about Testing Apex
Can we test asynchronous Apex using Salesforce Apex Test classes?

Yes, you can test asynchronous Apex code like future methods, batch Apex, and scheduled Apex. Salesforce provides a Test method called `Test.startTest()` and `Test.stopTest()` which allows the system to execute asynchronous code synchronously for testing purposes. When you call `Test.stopTest()`, all asynchronous processes kicked off after `Test.startTest()` are collected and executed before the test method finishes.

How can we handle test data setup for managed packages in Apex?

Test data setup for managed packages can be quite challenging, especially since the Apex SeeAllData annotation is set to false by default in order to ensure test isolation. It means tests do not have access to pre-existing data in the organization, such as standard Salesforce objects and custom objects. If you're developing a managed package, it's good practice to include Apex classes that provide setup methods specifically for generating test data. These can be used by any test methods included in the package, which helps in ensuring consistency and reducing redundancy.

How does Governor Limits impact testing in Apex?

Apex tests are subjected to Governor Limits, just like non-test classes. Salesforce enforces a number of runtime limits to ensure that runaway Apex doesn't monopolize shared resources. Tests that exceed governor limits fail. However, the `Test.startTest()` and `Test.stopTest()` methods give you a new set of governor limits during the test execution. Any code that is executed after the `Test.startTest()` method is subject to a fresh set of governor limits. This can be used strategically in tests to ensure that you have enough resources to test bulk or complex operations.

© All rights Reserved. 2023