Last Updated On : 28-Sep-2026
Salesforce Certified Platform Developer II (SP25) Practice Test
Prepare with our free Salesforce Certified Platform Developer II (SP25) sample questions and pass with confidence. Our Salesforce-Platform-Developer-II practice test is designed to help you succeed on exam day.
Salesforce 2026
A developer is building a Lightning web component that retrieves data from Salesforce and
assigns it to the record property:
JavaScript
import { LightningElement, api, wire } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
export default class Record extends LightningElement {
@api fields;
@api recordId;
record;
}
What must be done in the component to get the data from Salesforce?
A. Add @api(getRecord, { recordId: '$recordId' }) above record.
B. Add @wire(getRecord, { recordId: '$recordId' }) above record.
C. Add @api(getRecord, { recordId: '$recordId', fields: '$fields' }) above record.
D. Add @wire(getRecord, { recordId: '$recordId', fields: '$fields' }) above record.
Explanation:
This question tests the correct syntax for using the @wire decorator with the getRecord adapter in a Lightning Web Component to retrieve Salesforce data. The wire service automatically provisions data to the decorated property based on the configuration object passed to getRecord.
β
Correct Option:
D. @wire(getRecord, { recordId: '$recordId', fields: '$fields' })
The @wire decorator must wrap the adapter function (getRecord) along with a configuration object containing the required parameters. The recordId and fields properties are referenced dynamically using the $ prefix, which tells the wire service to re-evaluate when those reactive properties change. This is the only option that correctly uses @wire with both necessary parameters.
β Incorrect options:
A. @api(getRecord, { recordId: '$recordId' })
The @api decorator is used to make a property or method public for parent components, not to invoke wire adapters. Additionally, this option omits the required fields parameter, which getRecord needs to determine which fields to retrieve from the record.
B. @wire(getRecord, { recordId: '$recordId' })
This option uses the correct @wire decorator and dynamically references recordId, but it fails to include the fields parameter. Without specifying which fields to query, getRecord cannot retrieve the desired data, making this configuration incomplete.
C. @api(getRecord, { recordId: '$recordId', fields: '$fields' })
This option incorrectly uses the @api decorator instead of @wire. While it includes both parameters correctly, @api does not invoke wire adapters or provision data to properties. It is used solely for exposing public properties or methods.
π§ Reference:
β Salesforce Developers β Get Record Data β Confirms that @wire(getRecord, { recordId, fields }) is the correct syntax for retrieving record data with reactive parameters.
β Salesforce Developers β Data and Communication Anti-Patterns in Lightning Web Components β Confirms that @wire is for read operations and that getRecord requires a fields array specifying which fields to fetch.
An Apex trigger and Apex class increment a counter, `Edit_Count__c`, any time the Case
is changed.
```
java
public class CaseTriggerHandler {
public static void handle(List
for (Case c : cases) {
c.Edit_Count__c = c.Edit_Count__c + 1;
}
}
}
trigger on Case(before update) {
CaseTriggerHandler.handle(Trigger.new);
}
```
A new before-save record-triggered flow on the Case object was just created in production
for when a Case is created or updated. Since the process was added, there are reports
that `Edit_Count__c` is being incremented more than once for Case edits. Which Apex
code fixes this problem?
A. ```java
public class CaseTriggerHandler {
public static Boolean firstRun = true;
public static void handle(List
for (Case c : cases) {
B. Edit_Count__c = c.Edit_Count__c + 1;
}
}
}
trigger on Case(before update) {
CaseTriggerHandler.firstRun = true;
if (CaseTriggerHandler.firstRun) {
CaseTriggerHandler.handle(Trigger.newMap);
}
CaseTriggerHandler.firstRun = false;
}
```
C. ```java
public class CaseTriggerHandler {
public static Boolean firstRun = true;
public static void handle(List
for (Case c : cases) {
D. Edit_Count__c = c.Edit_Count__c + 1;
}
}
}
trigger on Case(before update) {
if (CaseTriggerHandler.firstRun) {
CaseTriggerHandler.handle(Trigger.new);
}
CaseTriggerHandler.firstRun = false;
}
```
E. ```java
public class CaseTriggerHandler {
Boolean firstRun = true;
public static void handle(List
if (firstRun) {
for (Case c : cases) {
F. Edit_Count__c = c.Edit_Count__c + 1;
}
}
firstRun = false;
}
}
trigger on Case(before update) {
CaseTriggerHandler.handle(Trigger.new);
}
```
G. ```java
trigger on Case(before update) {
Boolean firstRun = true;
if (firstRun) {
CaseTriggerHandler.handle(Trigger.newMap);
}
firstRun = false;
}
```
public class CaseTriggerHandler {
public static Boolean firstRun = true;
public static void handle(List
for (Case c : cases) {
B. Edit_Count__c = c.Edit_Count__c + 1;
}
}
}
trigger on Case(before update) {
CaseTriggerHandler.firstRun = true;
if (CaseTriggerHandler.firstRun) {
CaseTriggerHandler.handle(Trigger.newMap);
}
CaseTriggerHandler.firstRun = false;
}
```
Explanation
The issue is caused by the Case automation executing the counter logic more than once within the same transaction.
A static Boolean can act as a recursion guard because its value persists throughout the current Apex transaction.
The trigger should check the flag before executing the handler and set it to false afterward.
The flag must not be reset to true every time the trigger fires.
A local Boolean inside the trigger also cannot prevent a subsequent trigger execution.
Therefore, AβB is the correct code pattern.
Correct Option
π’ AβB. Use a static firstRun Boolean and set it to false after the handler executes.
This solution uses a static Boolean in the handler class as a transaction-level recursion guard. On the first trigger execution, firstRun is true, so the handler increments Edit_Count__c. The trigger then sets the flag to false. If the trigger executes again during the same transaction, the condition fails and the handler is skipped. Because Apex static variables retain their values within a transaction, this prevents duplicate trigger processing.
Incorrect Options
β CβD. Reset CaseTriggerHandler.firstRun to true inside the trigger.
This defeats the recursion guard. Every time the trigger executes, firstRun is first reset to true, so the condition always succeeds and the handler executes again. The static variable must retain its previous value instead of being reset on every trigger invocation.
β EβF. Declare firstRun as a non-static class variable.
This approach cannot be used as a transaction-level guard in the static handle() method because firstRun is an instance variable while handle() is static. More importantly, the guard must be shared across trigger invocations in the transaction, which is why a static variable is required.
β G. Declare firstRun as a local Boolean inside the trigger.
A local variable is initialized every time the trigger executes. Therefore, firstRun becomes true again on each invocation, allowing the handler to run repeatedly. A static class variable is required so its value persists across trigger executions within the same transaction.
Reference
β Before-Save Record-Triggered Flows β Confirms that before-save flows execute during the record-save process and can modify fields on the triggering record.
β Leveling Up Your Apex Skills β Confirms that Apex static variables are scoped to the transaction, supporting their use for transaction-level state such as recursion guards.
A developer is writing a Lightning Web component that queries accounts in the system and presents a lightning-datatable with the results. The users want to be able to filter the results based on up to five fields, that will vary according to their selections when running the page. Which feature of Apex code is required to facilitate this solution?
A. describeSObjects()
B. REST API
C. SOSL queries
D. Dynamic SOQL
Explanation:
The Lightning Web Component must display Accounts in a lightning-datatable and allow users to filter the results by up to five fields that can change at runtime based on the userβs selections. Because the set of filter fields (and their values) is not known until the page runs, the SOQL queryβs WHERE clause must be constructed programmatically. Dynamic SOQL (building a query string at runtime and executing it with Database.query or Database.queryWithBinds) is the Apex feature that enables this flexibility.
β
Correct Option:
β
D. Dynamic SOQL
In the Apex controller the developer builds a SOQL string that includes only the fields the user has chosen to filter on, binds the corresponding values safely, and executes the query with Database.query (or the safer Database.queryWithBinds). The resulting list of Accounts is returned to the LWC and bound to the lightning-datatable. This approach supports any combination of up to five filter fields without hard-coding multiple static queries.
β Incorrect options:
β A. describeSObjects()
Schema.DescribeSObjectResult (obtained via Schema.describeSObjects) is used to inspect object and field metadata. While it can help validate that a chosen filter field exists and is accessible, it does not execute a query or return record data.
β B. REST API
The REST API is an external interface. An LWC that needs data from the same org calls an Apex method directly; using the REST API would be unnecessary overhead and is not required for this use case.
β C. SOSL queries
SOSL is designed for free-text search across multiple objects and fields. It is not suited to structured, field-specific filtering (e.g., βIndustry = βTechnologyβ AND BillingState = βCAββ) that a datatable filter typically requires.
π§ Reference:
β Dynamic SOQL (Apex Developer Guide β Salesforce Developers)
Explains how to construct and execute SOQL strings at runtime with Database.query / Database.queryWithBinds, which is exactly what is needed when filter fields vary according to user input.
A company wants to incorporate a third-party web service to set the Address fields when an Account is inserted, if they have not already been set. What is the optimal way to achieve this?
A. Create a Before Save Flow, execute a Queueable job from it, and make a callout from the Queueable job.
B. Create an Apex class, execute a Batch Apex job from it, and make a callout from the Batch Apex job.
C. Create an Apex trigger, execute a Queueable job from it, and make a callout from the Queueable job.
D. Create an Apex class, execute a Future method from it, and make a callout from the Future method.
Explanation
This question tests how to make a callout to an external web service when a record is created.
Apex triggers cannot make synchronous callouts, so the callout must run asynchronously after the insert.
The best choice is the asynchronous tool that is flexible, lightweight, and started directly from the trigger.
β
C. Create an Apex trigger, execute a Queueable job from it, and make a callout from the Queueable job.
An Apex trigger on Account insert can find records with blank address fields and enqueue a Queueable job with their IDs. The Queueable class implements Database.AllowsCallouts, so it can call the third-party service asynchronously and then update the Address fields. Queueable Apex accepts complex parameters, handles bulk record sets, and supports chaining, which makes it the recommended pattern.
β A. Create a Before Save Flow, execute a Queueable job from it, and make a callout from the Queueable job.
A before-save record-triggered flow can only update field values on the triggering record. It cannot call Apex actions or launch a Queueable job, and the record does not yet exist in the database for the job to reference. Callouts also cannot run in the before-save context, so this design does not work.
β B. Create an Apex class, execute a Batch Apex job from it, and make a callout from the Batch Apex job.
Batch Apex is built for processing very large data volumes, not for reacting to individual inserts. Starting a batch job for every Account insert is heavy and can exhaust the limit of 100 jobs in the flex queue, delaying other jobs. It also runs later than needed, which makes it a poor fit for this requirement.
β D. Create an Apex class, execute a Future method from it, and make a callout from the Future method.
Future methods can make callouts, but they accept only primitive parameters, so sObjects cannot be passed in. They cannot be chained or monitored by job ID, and one future method cannot call another. A transaction is also limited to 50 future calls. Queueable Apex avoids these restrictions and is the preferred approach.
Reference
π Queueable Apex - Apex Developer Guide, Salesforce Developers β confirms Queueable jobs run asynchronously, accept non-primitive types, and support callouts with Database.AllowsCallouts.
π Future Methods - Apex Developer Guide, Salesforce Developers β confirms future methods accept only primitive parameters and cannot be chained.
A developer is responsible for formulating the deployment process for a Salesforce project. The project follows a source-driven development approach, and the developer wants to ensure efficient deployment and version control of the metadata changes. Which tool or mechanism should be utilized for managing the source-driven deployment process?
A. Data Loader
B. Change Sets
C. Salesforce CLI with Salesforce DX
D. Unmanaged Packages
Explanation:
This question tests the source-driven development model in Salesforce. In this model, metadata is stored as source files in a local Salesforce DX project and managed in a version-control system, rather than treating the production org as the primary source of truth.
Salesforce CLI supports retrieving metadata into source format, tracking changes in supported orgs, and deploying controlled metadata changes through source-driven development and CI/CD processes.
βοΈ Correct Option:
βοΈ Option C β Salesforce CLI with Salesforce DX
Salesforce CLI is the appropriate tool for source-driven deployment because it manages metadata as files in a Salesforce DX project. Developers can retrieve metadata with sf project retrieve start, store and version it in Git, validate deployments, run tests, and deploy source using sf project deploy start. Source tracking can identify differences between local project files and supported scratch orgs or sandboxes.
β Incorrect Options:
β Option A β Data Loader
Data Loader is used to import, export, update, delete, and upsert Salesforce record data. It does not manage metadata source files, track source changes, integrate metadata with version control, or deploy Apex, objects, permission sets, Lightning components, and other configuration between environments. Therefore, it is not a source-driven deployment mechanism.
β Option B β Change Sets
Change Sets provide an org-based mechanism for moving selected metadata between related Salesforce organizations, such as a sandbox and its production org. They do not provide local source representation, Git-based version control, automated deployment pipelines, or source tracking. Therefore, Change Sets do not support a modern source-driven development workflow.
β Option D β Unmanaged Packages
Unmanaged packages can distribute metadata but are not intended to manage an ongoing source-driven deployment lifecycle. They do not provide source tracking, version-control integration, automated validation, or continuous deployment controls. Once installed, their components can be modified directly in the target organization, which makes controlled release management more difficult.
π§ Reference:
β
Track Changes Between Your Project and Org
β Confirms that Salesforce CLI source tracking detects changes between a local project and supported scratch orgs or sandboxes.
β
project deploy start
β Confirms that Salesforce CLI deploys metadata from a local project and supports source-formatted metadata deployment.
| Page 1 out of 33 Pages |