1. Why is it a best practice to have only one trigger per object?
Salesforce doesn’t guarantee the execution order of multiple triggers on the same object, which can cause unpredictable behavior when logic depends on a specific sequence. A single trigger per object (delegating to a handler class) gives you full control over execution order.
2. What is a trigger handler pattern, and why use one?
A trigger handler pattern moves all business logic out of the trigger itself and into a separate Apex class, with the trigger simply calling handler methods based on the context (before insert, after update, etc.). This keeps triggers thin, makes logic testable in isolation, and avoids duplicated logic across multiple triggers.
trigger AccountTrigger on Account (before insert, after update) {
AccountTriggerHandler handler = new AccountTriggerHandler();
if (Trigger.isBefore && Trigger.isInsert) {
handler.beforeInsert(Trigger.new);
}
}3. What are some popular trigger frameworks used in the Salesforce ecosystem?
Common open-source frameworks include the Apex Trigger Actions Framework, Kevin O’Hara’s SObject Trigger Framework, and Andrew Fawcett’s fflib-based Enterprise Patterns. Most solve the same core problem: centralizing dispatch logic and preventing recursive or duplicate execution.
4. What is the correct order of execution when a record is saved in Salesforce?
At a high level: system validation rules run first, then before triggers, then custom validation rules, then duplicate rules, then the save to the database (but not committed yet), then after triggers, then assignment rules, auto-response rules, workflow rules, escalation rules, entitlement rules, and finally the commit to the database, followed by post-commit logic like sending emails.
5. Why does order of execution matter for interview scenarios?
Interviewers often test whether you understand that workflow field updates can re-trigger the save process, or that before-triggers happen before validation rules run — misunderstanding this can lead to unexpected infinite loops or values being overwritten unexpectedly.
6. How do you prevent a trigger from running recursively?
A common pattern is a static Boolean flag in a handler or utility class that’s set to true the first time the trigger logic runs in a transaction, checked at the start, and used to skip re-execution during any recursive invocation within that same transaction.
public class TriggerControl {
public static Boolean hasAlreadyRun = false;
}7. What is the difference between Trigger.isBefore and Trigger.isAfter?
Trigger.isBefore is true when the trigger is executing in a before-context (record not yet saved, so field changes are cheap), while Trigger.isAfter is true in an after-context (record already has its Id, but changing fields here requires a separate DML update).
8. When would you use an after-trigger instead of a before-trigger?
After-triggers are needed when you require the record’s Id (for example, to create related child records) or need to update related records rather than the triggering record itself. Before-triggers are preferred for simply modifying fields on the triggering record, since they avoid an extra DML statement.
9. What is a Test Class in Apex, and why is it required?
A test class contains methods annotated with @isTest that verify Apex code behaves correctly. Salesforce requires at least 75% code coverage across an org’s Apex code (with meaningful assertions, not just coverage) before code can be deployed to production.
@isTest
private class AccountTriggerHandlerTest {
@isTest
static void testAccountInsert() {
Account acc = new Account(Name = 'Test Account');
insert acc;
System.assertNotEquals(null, acc.Id);
}
}10. What is Test.startTest() and Test.stopTest() used for?
They mark a boundary around the code you actually want to measure and test — code inside this block gets a fresh set of governor limits, and any asynchronous Apex called inside it (Future, Queueable, Batch) executes synchronously when Test.stopTest() is reached, letting you assert on its results immediately.
Test.startTest();
MyQueueableClass.enqueueJob();
Test.stopTest();
System.assertEquals(1, [SELECT COUNT() FROM Task]);11. Why shouldn’t test classes rely on existing org data?
Tests that query existing records assume specific data exists, which may not be true in other environments (like a fresh sandbox or another org), causing tests to fail unpredictably. Best practice is to create all necessary test data within the test method itself, or through a @testSetup method.
12. What is @testSetup used for?
A method annotated with @testSetup runs once before all test methods in a class and creates common test data that each test method can then use, reducing duplicated setup code and improving test run performance.
@testSetup
static void setup() {
insert new Account(Name = 'Test Co');
}13. What is SeeAllData=true, and why is it generally discouraged?
It’s an annotation that allows a test method to access real org data instead of being isolated in its own data sandbox. It’s discouraged because it makes tests dependent on the state of the org’s data, which can change over time and cause tests to fail inconsistently across environments.
14. How do you test that an exception is thrown correctly?
Wrap the code that should throw the exception in a try-catch within the test method, and assert that the catch block was actually reached (or that no exception was silently swallowed):
try {
insert new Account(); // missing required field
System.assert(false, 'Expected an exception');
} catch (DmlException e) {
System.assert(e.getMessage().contains('REQUIRED_FIELD_MISSING'));
}15. What is code coverage, and is 75% coverage alone considered good practice?
Code coverage measures the percentage of Apex lines executed by test methods. While Salesforce enforces a minimum of 75% to deploy to production, coverage alone doesn’t guarantee correctness — tests should include meaningful assertions that verify actual behavior, not just execute lines without checking results.
16. What is the difference between unit tests and integration-style tests in Apex?
Unit tests isolate a single method or class’s logic, often using mock callouts or minimal data. Integration-style tests verify that multiple components (trigger, handler, related objects) work correctly together, closer to a real end-to-end scenario, though still run entirely within Salesforce’s test framework.
17. How do you test a class that makes an HTTP callout?
You implement the HttpCalloutMock interface to simulate the external response, then register it with Test.setMock() before the callout code runs, since real HTTP callouts aren’t allowed during test execution.
Test.setMock(HttpCalloutMock.class, new MyMockResponseGenerator());18. What is System.runAs() used for in test classes?
System.runAs() lets you execute test code in the context of a specific user, which is essential for testing sharing rules, field-level security, and profile-based permission behavior, since test methods otherwise run as the user executing the test (often an admin with full access).
User testUser = [SELECT Id FROM User WHERE Profile.Name = 'Standard User' LIMIT 1];
System.runAs(testUser) {
// logic tested with restricted permissions
}19. Why should test classes cover both positive and negative scenarios?
Testing only the “happy path” (valid data, expected flow) misses bugs that occur with invalid input, missing data, or edge cases like bulk operations or null values — these negative and boundary scenarios are often exactly what breaks in production and what interviewers expect you to mention.
20. What is a trigger’s context variable, Trigger.newMap, used for?
Trigger.newMap provides a map of record Id to the new version of the sObject, available in update, before/after contexts where an Id already exists. It’s useful for efficiently looking up a specific record’s new state by Id without looping through Trigger.new manually.
Account updatedAcc = Trigger.newMap.get(someAccountId);
No comments:
Post a Comment