Writing Test Classes and Methods
Writing Test Classes and Methods is a critical skill for Salesforce Platform Developer I certification. In Salesforce, test classes validate that your Apex code functions correctly and are required for deploying code to production, where a minimum of 75% code coverage is mandatory. **Test Class St… Writing Test Classes and Methods is a critical skill for Salesforce Platform Developer I certification. In Salesforce, test classes validate that your Apex code functions correctly and are required for deploying code to production, where a minimum of 75% code coverage is mandatory. **Test Class Structure:** Test classes are annotated with `@isTest`, which ensures they don't count against org's Apex code limits. Test methods are defined as `static void` and annotated with `@isTest` or using the `testMethod` keyword. ```apex @isTest private class MyTestClass { @isTest static void testMyMethod() { // Test logic here } } ``` **Key Principles:** 1. **Create Test Data:** Tests should create their own data rather than relying on existing org data. Use `@TestSetup` methods to create reusable test data across multiple test methods. 2. **Test.startTest() and Test.stopTest():** These methods reset governor limits within the test context, allowing you to test code against fresh limits. `stopTest()` also forces asynchronous operations to complete synchronously. 3. **System.assert Methods:** Use `System.assertEquals()`, `System.assertNotEquals()`, and `System.assert()` to verify expected outcomes. 4. **Positive and Negative Testing:** Test both valid scenarios (happy path) and invalid scenarios (error handling, boundary conditions). 5. **Bulk Testing:** Test with multiple records (typically 200+) to ensure code handles bulk operations properly. **Best Practices:** - Use `@isTest(SeeAllData=false)` (default) to isolate test data - Test different user profiles using `System.runAs()` - Cover trigger scenarios, exception handling, and edge cases - Avoid hardcoded IDs - Test with both single and bulk records **@TestSetup Example:** ```apex @TestSetup static void setupData() { Account acc = new Account(Name='Test'); insert acc; } ``` Test classes cannot be deployed to production themselves but are essential for validating business logic, ensuring governor limit compliance, and maintaining code quality throughout the development lifecycle.
Writing Test Classes and Methods – Salesforce Platform Developer 1 Exam Guide
Why Writing Test Classes and Methods Is Important
Testing is a fundamental aspect of Salesforce development. Salesforce enforces a minimum of 75% code coverage before any Apex code can be deployed to a production environment. Beyond meeting this requirement, well-written test classes ensure the reliability, correctness, and maintainability of your code. They help you catch bugs early, validate business logic, and protect against regressions when changes are made. On the Salesforce Platform Developer 1 (PD1) exam, questions on writing test classes and methods are virtually guaranteed, making this a critical topic to master.
What Are Test Classes and Methods?
A test class in Apex is a class annotated with @isTest that contains one or more test methods. Test methods are individual methods within a test class, also annotated with @isTest (or using the legacy testMethod keyword), that exercise and verify specific pieces of functionality in your Apex code.
Test classes and methods do not count against your organization's Apex code limits (governor limits on total code size). They also do not count toward code coverage themselves — they exist solely to test other code.
How Test Classes Work
Here is the anatomy of a typical test class:
@isTest
private class MyControllerTest {
@TestSetup
static void setupData() {
// Create test data used across multiple test methods
Account acc = new Account(Name = 'Test Account');
insert acc;
}
@isTest
static void testPositiveScenario() {
Account acc = [SELECT Id, Name FROM Account LIMIT 1];
Test.startTest();
String result = MyController.doSomething(acc.Id);
Test.stopTest();
System.assertEquals('Expected Value', result, 'The result should match the expected value');
}
@isTest
static void testNegativeScenario() {
Test.startTest();
try {
String result = MyController.doSomething(null);
System.assert(false, 'An exception should have been thrown');
} catch (Exception e) {
System.assert(e.getMessage().contains('error'), 'Exception message should contain error');
}
Test.stopTest();
}
}
Key Concepts Explained:
1. The @isTest Annotation
This annotation marks a class or method as a test. Classes annotated with @isTest are not counted against the org's code size limit. Methods inside must be static and return void.
2. @TestSetup Methods
A method annotated with @TestSetup runs once before all test methods in the class. It creates common test data that is rolled back and re-provided fresh for each test method. This improves efficiency and consistency. There can only be one @TestSetup method per test class.
3. Test.startTest() and Test.stopTest()
These methods reset governor limits, giving the code between them a fresh set of governor limits. This is critical for testing code that may approach governor limits. Test.stopTest() also forces asynchronous operations (such as @future methods, queueable jobs, batch Apex, and scheduled Apex) to execute synchronously, allowing you to verify their results immediately after.
4. System.assert Methods
Salesforce provides three primary assertion methods:
- System.assert(condition, message) — asserts that a condition is true
- System.assertEquals(expected, actual, message) — asserts that two values are equal
- System.assertNotEquals(notExpected, actual, message) — asserts that two values are not equal
Always include meaningful assertion messages. Tests without assertions technically pass, but they do not truly validate behavior — and the exam expects you to know that assertions are essential.
5. Creating Test Data
By default, test methods do not have access to real organization data (with few exceptions like User, Profile, RecordType, and certain metadata objects). You must create your own test data. Best practices include:
- Using @TestSetup for shared data creation
- Creating dedicated Test Data Factory (utility) classes to generate reusable test records
- Avoiding hardcoded record IDs
- Creating data that covers both positive and negative scenarios
- Creating bulk data (e.g., 200+ records) to test trigger bulkification
6. SeeAllData=true
The annotation @isTest(SeeAllData=true) allows a test to access all data in the organization. This is generally considered a bad practice because it makes tests dependent on existing data, which can cause failures in different orgs. It should only be used when absolutely necessary (e.g., testing against standard price book entries in some scenarios).
7. Testing Different Scenarios
Good test coverage includes:
- Positive tests: Valid input produces the expected result
- Negative tests: Invalid input is handled gracefully (exceptions, error messages)
- Bulk tests: Processing 200+ records to ensure triggers and code handle bulk operations
- User/permission tests: Using System.runAs() to test code execution under different user profiles and permission sets
- Boundary tests: Testing edge cases and limit scenarios
8. System.runAs()
This method allows you to run a block of code as a specific user. It is commonly used to:
- Test record sharing and visibility
- Test field-level security
- Verify that a user with or without certain permissions behaves correctly
- Note: System.runAs() does not enforce CRUD/FLS on its own; it simulates the running user context.
9. Testing Asynchronous Apex
When testing @future, Queueable, Batch, or Schedulable classes:
- Enqueue or invoke the async operation between Test.startTest() and Test.stopTest()
- The async code executes synchronously when Test.stopTest() is called
- Place assertions after Test.stopTest() to verify results
10. Testing Callouts
You cannot make real HTTP callouts in test methods. Instead, you must implement the HttpCalloutMock interface (or StaticResourceCalloutMock) and set it using Test.setMock() before invoking code that makes callouts.
11. Code Coverage Requirements
- Minimum 75% overall code coverage is required for deployment to production
- Each trigger must have at least 1% coverage (though best practice is much higher)
- Code coverage is measured by the number of executable lines of code exercised by test methods
- Lines inside test classes themselves are not counted in coverage calculations
- Comments, blank lines, and System.debug statements are not counted as executable lines
Best Practices Summary:
- Always use assertions in every test method
- Create your own test data; avoid SeeAllData=true
- Use @TestSetup and Test Data Factory patterns for efficiency
- Test positive, negative, bulk, and permission-based scenarios
- Use Test.startTest()/Test.stopTest() to isolate governor limits and execute async code
- Aim for high-quality coverage, not just high-percentage coverage
- Never depend on hardcoded IDs or org-specific data
- Use System.runAs() to test different user contexts
Exam Tips: Answering Questions on Writing Test Classes and Methods
Tip 1: Remember that test classes must be annotated with @isTest and test methods must be static void. If an exam question shows a test method that returns a value or is non-static, it is invalid.
Tip 2: Know that Test.stopTest() forces all asynchronous operations to complete. Questions often test whether you know where to place assertions — always after Test.stopTest() for async code.
Tip 3: Understand that @TestSetup runs once, but each test method gets its own copy of the data (data is rolled back between tests). If a question asks whether changes in one test method affect another, the answer is no.
Tip 4: When a question mentions deploying to production, recall the 75% minimum code coverage rule. Also remember that every trigger must have some coverage.
Tip 5: If a question involves testing callouts, look for Test.setMock() and an implementation of HttpCalloutMock. Without these, the test will fail with a callout exception.
Tip 6: Questions may present code without assertions and ask what is wrong. The answer is typically that the test does not verify expected behavior — assertions are required for meaningful tests.
Tip 7: Be cautious of SeeAllData=true in answer choices. The exam generally favors creating test data explicitly. If an option uses SeeAllData=true when it is not necessary, it is likely the wrong answer.
Tip 8: For bulk testing questions, remember that Salesforce triggers should be tested with at least 200 records to validate proper bulkification and governor limit compliance.
Tip 9: Know the difference between System.assert(), System.assertEquals(), and System.assertNotEquals(). Questions may test whether you can choose the appropriate assertion for a given scenario.
Tip 10: When a question involves testing with different user profiles or permissions, System.runAs() is almost always the correct approach. Remember that creating a User record in a test does not require a DML commit to use it with System.runAs() — but you do typically insert it first.
Tip 11: Pay close attention to the question context. If it asks about best practice, favor answers that include test data factories, @TestSetup, proper assertions, and bulk testing. If it asks what is required, focus on the 75% coverage rule and the @isTest annotation.
Tip 12: Test methods cannot be used to send emails, make callouts (without mocks), or commit data to the database permanently. All DML operations in test methods are automatically rolled back after execution.
🎓 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!