Testing Triggers, Controllers, and Flows
Testing Triggers, Controllers, and Flows is a critical component of Salesforce development and the Platform Developer I certification. Salesforce requires a minimum of 75% code coverage for deployment, making robust testing essential. **Testing Triggers:** Trigger tests validate that automation lo… Testing Triggers, Controllers, and Flows is a critical component of Salesforce development and the Platform Developer I certification. Salesforce requires a minimum of 75% code coverage for deployment, making robust testing essential. **Testing Triggers:** Trigger tests validate that automation logic fires correctly on DML operations (insert, update, delete, undelete). Best practices include testing bulk operations (200+ records), verifying positive and negative scenarios, and ensuring trigger handlers execute properly. Use @isTest annotation, Test.startTest()/Test.stopTest() to reset governor limits, and System.assert() methods to validate outcomes. Always test with different user profiles to verify sharing and security rules. **Testing Controllers:** For Standard Controllers, test extensions by instantiating the StandardController with a test record and passing it to the extension. For Custom Controllers, test all action methods, getter/setter properties, and page references. Use Test.setCurrentPage() to set the PageReference context, and ApexPages.currentPage().getParameters() to simulate URL parameters. Verify that controller methods return correct PageReferences, error messages via ApexPages.hasMessages(), and view state data. **Testing Flows:** Flows (Screen Flows, Record-Triggered Flows, Autolaunched Flows) can be tested using Apex tests. Use Flow.Interview to invoke flows programmatically, passing input variables and verifying output variables. For Record-Triggered Flows, create test data that meets flow entry criteria and verify the expected automation results. Test exception handling and fault paths to ensure graceful error management. **Best Practices:** - Use @TestSetup methods to create reusable test data - Never use SeeAllData=true unless absolutely necessary - Test governor limit compliance with bulk data - Validate both expected successes and expected failures - Use System.assertEquals() with meaningful error messages - Test as different user contexts using System.runAs() - Mock external callouts using HttpCalloutMock interface Comprehensive testing ensures reliable deployments, prevents regressions, and maintains data integrity across your Salesforce org.
Testing Triggers, Controllers, and Flows – Salesforce Platform Developer 1 Exam Guide
Why Is Testing Triggers, Controllers, and Flows Important?
Salesforce enforces a minimum of 75% code coverage before any Apex code can be deployed to a production environment. This requirement exists because untested code can introduce bugs, data corruption, security vulnerabilities, and governor limit violations. Testing triggers, controllers, and flows ensures that your automation logic behaves correctly under expected and unexpected conditions, handles bulk data properly, and respects Salesforce governor limits. For the Platform Developer 1 (PD1) exam, understanding how to write effective test classes and validate declarative automation is a core competency.
What Is Testing in Salesforce?
Testing in Salesforce refers to writing Apex test classes and test methods that exercise your code to verify it produces the correct results. Salesforce uses a built-in testing framework based on the @isTest annotation. Testing also extends to validating the behavior of declarative tools like Flows by invoking them through Apex or by performing DML operations that trigger them.
The three major areas covered in this topic are:
1. Testing Triggers
Apex triggers fire on DML events (insert, update, delete, undelete). To test a trigger, you perform DML operations in a test method and then use System.assert() statements to verify the trigger produced the expected outcome.
2. Testing Controllers
Controllers (Standard Controllers, Custom Controllers, and Controller Extensions) manage the logic behind Visualforce pages and Lightning components. Testing controllers involves instantiating the controller, setting page parameters, invoking action methods, and asserting results.
3. Testing Flows
While Flows themselves are declarative and don't require code coverage, any Apex Invocable Actions called by a Flow do need test coverage. Additionally, you can indirectly test flows by performing DML operations in test methods that trigger Record-Triggered Flows and verifying the results.
How Testing Works – Key Concepts
Test Class Structure
A test class is annotated with @isTest and contains one or more test methods annotated with @isTest (or the legacy testMethod keyword). Test classes do not count against your org's Apex code size limit.
Example skeleton:
@isTest
private class MyTriggerTest {
@isTest
static void testInsertBehavior() {
// Setup test data
// Perform DML
// Assert results
}
}
Test Data Best Practices
- Always create your own test data inside the test method or use a @TestSetup method. Do not rely on existing org data unless using @isTest(SeeAllData=true), which should be avoided whenever possible.
- Use @TestSetup to create data once and share it across multiple test methods in the same class, improving efficiency.
- Create data in bulk (e.g., 200 records) to test trigger bulkification and governor limits.
Testing Triggers in Detail
To test a trigger, you simulate the DML event that causes the trigger to fire:
1. Create test records with the necessary field values.
2. Perform DML (insert, update, delete, or undelete) inside Test.startTest() and Test.stopTest() to get a fresh set of governor limits.
3. Query the records after DML to verify the trigger logic executed correctly.
4. Use System.assertEquals() or System.assert() to validate expected outcomes.
Key points:
- Test both positive scenarios (valid data, expected behavior) and negative scenarios (invalid data, error handling, try-catch blocks).
- Test with bulk records (at least 200) to ensure the trigger doesn't hit governor limits.
- Verify that before triggers modify field values correctly and after triggers create or update related records as expected.
Testing Controllers in Detail
Custom Controllers:
1. Instantiate the controller class directly in the test method.
2. Set any page parameters using ApexPages.currentPage().getParameters().put('key','value').
3. Call action methods and verify return values (e.g., PageReference).
4. Assert state changes in the controller's properties.
Controller Extensions:
1. Create a StandardController instance by passing a record: ApexPages.StandardController sc = new ApexPages.StandardController(myRecord);
2. Instantiate the extension: MyExtension ext = new MyExtension(sc);
3. Call methods and assert results.
Key points:
- Test methods that add ApexPages.Message entries and verify them with ApexPages.hasMessages().
- Test navigation by checking the returned PageReference URL.
- Cover all conditional branches in your controller logic.
Testing Flows in Detail
- Record-Triggered Flows are tested indirectly by inserting or updating records in Apex test methods and asserting the flow's expected side effects (e.g., field updates, new related records).
- Autolaunched Flows can be invoked in tests using Flow.Interview class: create a new interview, set input variables, and call start().
- Any Invocable Apex methods (annotated with @InvocableMethod) called by flows must have direct Apex test coverage.
- Screen Flows cannot be directly tested via Apex; focus on testing any underlying Apex actions they invoke.
Governor Limits and Test Context
- Test.startTest() and Test.stopTest() reset governor limits, giving your tested code a fresh context. Place your DML or callout logic between these two calls.
- Test.stopTest() also forces asynchronous operations (future methods, queueable jobs, batch jobs) to execute synchronously so you can assert their results immediately.
- Test methods run in their own transaction and do not commit data to the database – all data is rolled back automatically.
Assertions – The Heart of Testing
A test without assertions is incomplete. The three primary assertion methods are:
- System.assertEquals(expected, actual) – Verifies two values are equal.
- System.assertNotEquals(expected, actual) – Verifies two values are not equal.
- System.assert(condition) – Verifies a Boolean condition is true.
Always include a descriptive message as the third parameter: System.assertEquals(5, results.size(), 'Expected 5 records to be created by the trigger');
Exam Tips: Answering Questions on Testing Triggers, Controllers, and Flows
1. Know the 75% Rule: Salesforce requires at least 75% overall code coverage to deploy to production, and every trigger must have at least 1% coverage. However, best practice is to aim for 100% meaningful coverage.
2. @isTest(SeeAllData=true) vs. @TestSetup: Exam questions often test whether you understand that SeeAllData=true is discouraged and that @TestSetup is the recommended way to create reusable test data. Remember that @TestSetup methods run once per test class, and each test method gets its own copy of the data (rolled back after each method).
3. Test.startTest() / Test.stopTest(): If a question involves governor limits or asynchronous Apex, look for answers that correctly place logic between these calls. Remember that Test.stopTest() forces async execution to complete.
4. Bulk Testing: The exam frequently asks about testing triggers with bulk data. The correct approach is to insert 200+ records in a single DML statement to validate bulkification.
5. Positive and Negative Testing: Expect questions that differentiate between testing the "happy path" (valid inputs, expected behavior) and testing error conditions (invalid data, exceptions, validation rule violations). Use try-catch with System.assert in the catch block for negative tests, or use Database.insert(records, false) and check results.
6. Testing Controller Extensions: If the exam asks how to test a controller extension, the correct pattern is to create a StandardController wrapping a record, then pass it to the extension's constructor. Don't forget to set the current page and parameters if needed.
7. Flows Don't Need Code Coverage (But Their Apex Does): Flows are metadata, not Apex, so they don't contribute to or require code coverage. However, any Invocable Apex they call does need coverage. Exam questions may try to trick you into thinking flows need direct test coverage.
8. Mock Callouts: If a trigger or controller makes an HTTP callout, remember that callouts are not allowed in test context unless you implement HttpCalloutMock or Test.setMock(). This is a common exam topic.
9. RunAs: Use System.runAs(user) to test code that depends on user permissions, profiles, or sharing rules. This is especially important for controller tests that enforce FLS or CRUD checks.
10. Mixed DML Workaround: If your test needs to create a User and then perform DML on a non-setup object, use System.runAs() to separate the setup and non-setup DML operations.
11. Understand What Deploys Together: Triggers and their test classes deploy together. If a trigger has no test class, deployment fails. Know that test classes themselves do not require test coverage.
12. Read Questions Carefully: Many exam questions present code snippets and ask why a test is failing or what assertion is missing. Look for:
- Missing System.assert statements (the test passes but doesn't verify anything).
- Missing Test.stopTest() before assertions on async results.
- Data creation that doesn't match trigger filter criteria.
- Queries that return no results because data was created with wrong field values.
13. Key Vocabulary: Be comfortable with terms like test isolation, test data factory, code coverage, assertion, bulk testing, and test context. These appear frequently in exam questions.
Summary
Testing triggers, controllers, and flows is fundamental to Salesforce development and the PD1 exam. Focus on creating isolated test data, testing in bulk, using proper assertions, and understanding the boundary between declarative (Flows) and programmatic (Apex) testing. Master the patterns for testing controller extensions and triggers with both positive and negative scenarios, and always ensure your tests are meaningful – not just lines of code that execute without verifying outcomes.
🎓 Unlock Premium Access
Salesforce Certified Platform Developer I + ALL Certifications
- 🎓 Access to ALL Certifications: Study for any certification on our platform with one subscription
- 2750 Superior-grade Salesforce Certified Platform Developer I practice questions
- Unlimited practice tests across all certifications
- Detailed explanations for every question
- PD1: 5 full exams plus all other certification exams
- 100% Satisfaction Guaranteed: Full refund if unsatisfied
- Risk-Free: 7-day free trial with all premium features!