System.runAs() and Security Testing
System.runAs() is a critical method in Apex testing that allows developers to execute code in the context of a specific user, enabling robust security testing within Salesforce. This method is essential for verifying that sharing rules, field-level security, and profile-based permissions function c… System.runAs() is a critical method in Apex testing that allows developers to execute code in the context of a specific user, enabling robust security testing within Salesforce. This method is essential for verifying that sharing rules, field-level security, and profile-based permissions function correctly. **System.runAs() Overview:** By default, Apex test methods run under the context of the user executing the tests, with system-level permissions. System.runAs() overrides this behavior by simulating a specific user's access level. It accepts a User object as a parameter and executes the enclosed code block as that user. **Key Features:** - It does NOT enforce field-level security (FLS) or CRUD permissions in Apex code by default, but it DOES enforce sharing rules and record-level access. - It resets governor limits within its block, providing a fresh set of limits. - It allows testing with users created within the test method itself, without requiring DML commits for the user record outside the runAs block. - It is only available in test methods; it cannot be used in production Apex code. **Security Testing Applications:** 1. **Profile-Based Testing:** Create users with different profiles and verify they can only access appropriate records and functionality. 2. **Sharing Rule Validation:** Test that organization-wide defaults and sharing rules properly restrict or grant record access. 3. **Role Hierarchy Testing:** Verify that users higher in the role hierarchy can access subordinates' records. 4. **Permission Set Testing:** Assign permission sets to test users and validate elevated access scenarios. **Example Pattern:** ```apex User testUser = new User(ProfileId = restrictedProfileId, ...); System.runAs(testUser) { // Code runs as testUser // Assert that restricted operations fail // Assert that permitted operations succeed } ``` **Best Practices:** - Always test both positive (access granted) and negative (access denied) scenarios. - Combine System.runAs() with assertions to validate expected behavior. - Use separate test methods for different user contexts to maintain clarity. System.runAs() is indispensable for ensuring your application respects Salesforce's security model and data access controls.
System.runAs() and Security Testing in Salesforce – Complete Guide
Introduction
Security is a cornerstone of the Salesforce platform. When developing custom Apex code, it is critical to ensure that your logic respects the platform's security model — including profiles, permission sets, sharing rules, and field-level security. The System.runAs() method is a powerful testing utility that allows developers to simulate different user contexts within test methods, enabling thorough security testing without creating expensive or complex test setups.
Why Is System.runAs() Important?
1. Enforcing Least Privilege: Salesforce follows a security model where users should only access data they are authorized to see. System.runAs() lets you verify that your code honors these restrictions for different user profiles and roles.
2. Test Coverage for Multiple Personas: In real-world applications, different users (e.g., System Administrators, Standard Users, Community Users) interact with the same code. Without System.runAs(), all test code runs in the context of the running user (typically the developer or the user who triggers the test), which does not adequately test how the code behaves for less-privileged users.
3. Governor Limit Exception: System.runAs() does not count against the mixed DML operations governor limit when switching between setup and non-setup objects. This is a critical benefit that the exam frequently tests.
4. Compliance and Audit Requirements: Organizations often need to demonstrate that their custom code enforces security boundaries properly. Tests using System.runAs() serve as documented proof that security testing has been performed.
What Is System.runAs()?
System.runAs() is a method available only in test methods (classes annotated with @isTest or methods annotated with @isTest / testMethod keyword). It accepts a User record as a parameter and executes the code inside its block as if that user were the current running user.
Key characteristics:
- It can only be used in test context (test classes / test methods).
- It enforces the user's profile permissions, role hierarchy, and sharing rules.
- It does NOT enforce field-level security (FLS) or object-level CRUD permissions in Apex by default. Apex generally runs in system mode, meaning CRUD and FLS are not automatically enforced even inside runAs(). However, sharing rules (record-level access) ARE enforced if the class uses with sharing.
- Multiple System.runAs() blocks can be nested or used sequentially in a single test method.
- It resets the context for governor limits related to mixed DML operations.
How Does System.runAs() Work?
Here is the general pattern:
Step 1: Create or query a User record in the test method.
You typically create a test user with a specific profile:
Profile p = [SELECT Id FROM Profile WHERE Name = 'Standard User'];
User testUser = new User(
Alias = 'standt',
Email = 'standarduser@testorg.com',
EmailEncodingKey = 'UTF-8',
LastName = 'Testing',
LanguageLocaleKey = 'en_US',
LocaleSidKey = 'en_US',
ProfileId = p.Id,
TimeZoneSidKey = 'America/Los_Angeles',
UserName = 'standarduser' + DateTime.now().getTime() + '@testorg.com'
);
insert testUser;
Step 2: Use System.runAs() with the user.
System.runAs(testUser) {
// Code here runs as testUser
// Insert records, call methods, perform assertions
Account acc = new Account(Name = 'Test Account');
insert acc;
// Assert expected behavior
}
Step 3: Assert security behavior.
Inside the runAs block, you verify that:
- Users with restrictive profiles cannot access certain records (if the class uses with sharing).
- Sharing rules are respected.
- The correct exceptions are thrown when unauthorized actions are attempted.
What System.runAs() Enforces vs. What It Does Not
✅ Enforced:
- Record-level security (sharing rules) — but only if the Apex class declares with sharing
- Role hierarchy for record access
- Profile-based record type assignments
- User context (UserInfo.getUserId() returns the runAs user)
❌ NOT Enforced by Default:
- Object-level CRUD permissions (Apex runs in system mode)
- Field-level security (FLS) — unless you explicitly use methods like Security.stripInaccessible() or Schema.sObjectType describe calls
- Apex code that uses without sharing will bypass sharing rules even inside runAs()
System.runAs() and Mixed DML Operations
One of the most commonly tested aspects on the Platform Developer I exam is how System.runAs() helps with mixed DML issues.
Salesforce does not allow DML operations on setup objects (like User, Group, QueueSObject) and non-setup objects (like Account, Contact) in the same transaction. However, if you place the DML for non-setup objects inside a System.runAs() block after performing DML on setup objects, the governor limit for mixed DML is bypassed.
Example:
@isTest
static void testMixedDML() {
Profile p = [SELECT Id FROM Profile WHERE Name = 'Standard User'];
User u = new User(/* fields */);
insert u; // DML on setup object
System.runAs(u) {
Account a = new Account(Name = 'Test');
insert a; // DML on non-setup object — no mixed DML error!
}
}
Security Testing Best Practices
1. Always test with restrictive profiles: Don't just test with System Administrator. Create users with Standard User or custom profiles that have limited permissions.
2. Test with sharing keyword combinations: If your class uses with sharing, write tests to prove that users without record access cannot see or modify restricted records.
3. Use Security.stripInaccessible(): In API version 48.0+, use this method to enforce FLS programmatically, and write tests to validate that inaccessible fields are stripped.
4. Test permission set assignments: You can assign permission sets inside test methods and use runAs() to verify that the additional permissions work correctly.
5. Test negative scenarios: Verify that appropriate exceptions (like QueryException or DmlException with INSUFFICIENT_ACCESS) are thrown when unauthorized users attempt restricted operations.
Common Exam Scenarios
Scenario 1: A question asks how to test that a Standard User cannot access records owned by another user when the Apex class uses with sharing. The answer involves using System.runAs() with a Standard User and asserting that the query returns no records or that a DML operation fails.
Scenario 2: A question describes a mixed DML error in a test class. The fix is to wrap the non-setup object DML in a System.runAs() block.
Scenario 3: A question asks where System.runAs() can be used. The answer is only in test methods — it cannot be used in production Apex code.
Scenario 4: A question asks whether System.runAs() enforces FLS. The answer is no — Apex runs in system mode, and FLS is not automatically enforced even within runAs() blocks.
Exam Tips: Answering Questions on System.runAs() and Security Testing
🔹 Tip 1: Remember that System.runAs() is exclusively available in test methods. If an exam question suggests using it in a trigger or a controller, that option is incorrect.
🔹 Tip 2: System.runAs() enforces sharing rules and role hierarchy but does NOT enforce CRUD or FLS. If a question asks what security System.runAs() enforces, focus on record-level access, not field-level or object-level.
🔹 Tip 3: When you see a question about mixed DML errors in test classes, look for System.runAs() as the solution. Wrapping non-setup DML inside a runAs() block avoids the mixed DML exception.
🔹 Tip 4: Understand the difference between with sharing and without sharing. System.runAs() changes the user context, but if the Apex class is declared without sharing, sharing rules are still bypassed even inside a runAs() block.
🔹 Tip 5: If a question asks about testing that a user with a specific profile cannot perform an action, the correct approach is to create a User with that profile, insert it, and use System.runAs(thatUser) to execute the code and assert the expected behavior (e.g., an exception is thrown or no records are returned).
🔹 Tip 6: System.runAs() can be called multiple times in a single test method with different users. You do not need separate test methods for each user context.
🔹 Tip 7: The User record used in System.runAs() must be inserted into the database before being passed to the method (you cannot use a User record that has not been committed via DML).
🔹 Tip 8: Pay attention to questions about Security.stripInaccessible(). This is a separate mechanism for enforcing FLS in Apex and is tested independently from System.runAs(). If a question asks how to enforce FLS in Apex, the answer is stripInaccessible() or WITH SECURITY_ENFORCED in SOQL — not System.runAs().
🔹 Tip 9: Remember that @isTest(SeeAllData=true) and System.runAs() are independent concepts. SeeAllData controls whether test methods can see existing org data. System.runAs() controls the user context under which the test code runs.
🔹 Tip 10: When in doubt on the exam, recall the core purpose of System.runAs(): it exists to simulate running code as a specific user to test how the application behaves under different security contexts. Any answer that aligns with this purpose is likely correct.
Summary
System.runAs() is an essential tool for Salesforce developers to write robust, security-aware test methods. It allows simulation of different user contexts, helps avoid mixed DML errors, and enables verification that sharing rules and role hierarchies are properly respected. For the Platform Developer I exam, focus on understanding what it enforces (record-level sharing), what it doesn't enforce (CRUD/FLS), where it can be used (test methods only), and how it solves mixed DML issues. Mastering these concepts will help you confidently answer any exam question on this topic.
🎓 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!