Apex practice drills
98 short Salesforce Apex exercises in 9 modules. Each drill gives you a real task and starter code. You write the Apex, run it in your own Developer org, and ApexSensei checks the result.
The first two modules (36 drills) are free to run. Every drill is free to read.
Write Apex
Variables, numbers, text, dates, if and switch, loops, Lists, Sets and Maps. Your Apex foundation. 26 drills, beginner, free to run.
- Comments that survive review (Beginner, 5 min)
Explain why an Account field is set, switch off scratch logs, and keep the useful log running. - Hello, ApexSensei (Beginner, 5 min)
Run your first Apex statement in Salesforce and read its labeled debug output. - Quick win: double a number (Beginner, 5 min)
Write a one-line method that returns a number times two. Your first method that returns a value. - Quick win: greet by name (Beginner, 5 min)
Join text and a variable with + to build a greeting for any name. - Quick win: is it positive? (Beginner, 5 min)
Return true or false from a comparison. Meet the Boolean type and see what happens with null. - Quick win: make initials (Beginner, 5 min)
Take the first letter of two names, make them capitals and join them. - Quick win: pick the larger number (Beginner, 5 min)
Return the bigger of two numbers. Your first if. - Quick win: shout it (Beginner, 5 min)
Turn text into capitals and add an exclamation mark. - Quick win: join a full name (Beginner, 5 min)
Put a first and last name together with one space between them. - Quick win: is the name blank? (Beginner, 5 min)
Use String.isBlank to treat null, empty text and spaces the same way. - Integer division and Math.mod (Beginner, 10 min)
Count full seat packs with integer division and leftover seats with Math.mod for two order sizes. - Money is a Decimal (Beginner, 10 min)
Round Opportunity tax and total with HALF_UP, and see how plain setScale(2) rounds a tie differently. - True, false, and null (Beginner, 12 min)
Give a missing Lead score a default before doing math, then decide if the Lead is hot in three cases. - Clean a Contact name (Beginner, 12 min)
Trim spaces, fix capital letters, and drop a temporary suffix from imported Contact names. - Parse an email (Beginner, 12 min)
Split Contact emails at the @ into mailbox and domain, and handle an email with no @ safely. - Split and join text (Beginner, 12 min)
Split a multi-select picklist value into a List, add a value, join it for display, and split on dots safely. - Working with dates (Beginner, 10 min)
Build Dates, cross a year end, add a month to Jan 31, and count the days between two dates. - Score bands with else if (Beginner, 12 min)
Write a method that sorts a Lead score into Hot, Warm, Cold or Unscored with if / else if / else. - Switch on Case status (Beginner, 10 min)
Pick an action for Case statuses with switch on, a when else default, and a status typed in lowercase. - For loop: number invoices (Beginner, 10 min)
Number three invoices with a for loop, count the passes, and run the loop again with zero invoices. - For-each over Accounts (Beginner, 12 min)
Loop over in-memory Accounts with for-each, count them, and handle an empty List and a null List safely. - While a queue has work (Beginner, 12 min)
Work through an in-memory queue from the front with a while loop until it is empty, and show an empty queue does no work. - Lists of Opportunities (Beginner, 15 min)
Add, read and change in-memory Opportunities in a List, and read items only at safe positions. - Maps: Id to Account (Beginner, 15 min)
Build an Id-to-Account Map from a List, look up known and missing keys, and loop over every key. - Remove duplicate emails with a Set (Beginner, 12 min)
Write a method that counts different emails with a Set<String>, ignoring letter case and skipping empty entries. - Count Leads by Rating with a Map (Beginner, 15 min)
Write a method that counts Leads per Rating in a Map<String, Integer>, then loop over its keys to print each count.
Think in classes
Classes, constructors, methods, access modifiers, exceptions and enums. 10 drills, beginner, free to run.
- Build a Case summary class (Beginner, 10 min)
Write a small class, create separate Case summary objects, and call a method on each one. - Build queue labels with parameters (Beginner, 10 min)
Pass different owner names and Case counts into one reusable method. - Return a value and record work (Beginner, 12 min)
Contrast a method that returns a Lead action with a void method that changes state. - Initialize an appointment plan (Beginner, 12 min)
Use a constructor to set up an appointment plan and enforce a minimum length. - Protect a support ticket subject (Beginner, 12 min)
Keep ticket fields private and change them only through a public method that checks the input. - Expose a read-only quote total (Intermediate, 15 min)
Use Apex properties to protect a subtotal and work out a read-only total with tax. - Overload a Case label method (Intermediate, 15 min)
Give one method several parameter lists for normal, urgent, and aged Cases. - Route Cases with an enum (Intermediate, 15 min)
Model a fixed set of Case priorities, route each one with switch, and loop over them with values(). - Recover from division by zero (Intermediate, 15 min)
Call a calculator inside try/catch, catch the divide-by-zero MathException, and keep going. - Reject an invalid discount (Intermediate, 18 min)
Throw a custom exception when a discount is outside 0 to 50 percent, and print its message.
Query the org (SOQL)
SOQL and SOSL: filters, bind variables, relationships, aggregates, date literals and safe dynamic queries. 15 drills, intermediate, run with Apex Path.
- Query one Account (Beginner, 8 min)
Create one tagged Account, select its Id and Name, and verify the exact record returned. - Filter Account names (Beginner, 12 min)
Build a LIKE pattern that finds tagged Accounts whose names start with North, in any letter case, and skips close decoys. - Rank practice Accounts (Beginner, 10 min)
Sort four tagged Account names descending and keep only the first two records. - Bind an Account name (Beginner, 12 min)
Bind Apex String values into SOQL, including a name with an apostrophe and a name that matches nothing. - Query with a Set of Ids (Beginner, 12 min)
Collect Account Ids from a list of Contacts into a Set, then load those Accounts with one IN query. - Find missing Contact emails (Beginner, 12 min)
Query tagged Contacts with and without an Email, including the edge case of a blank Email. - Read a Contact's Account (Intermediate, 14 min)
Read Account.Name through each tagged Contact, and use it safely for a Contact that has no Account. - Load Account Contacts (Intermediate, 15 min)
Use a parent-to-child subquery to load two tagged Accounts with their Contacts, including an Account with none. - Find Accounts by Contact (Intermediate, 15 min)
Use IN and NOT IN with a Contact subquery to find tagged Accounts that do and don't have a matching Contact. - Summarize Opportunity amounts (Intermediate, 15 min)
Compare row and non-null counts, then calculate SUM and AVG for tagged Opportunities. - Keep busy Account groups (Intermediate, 18 min)
Group tagged Opportunities by Account and keep groups with at least two rows. - Loop over Accounts in chunks (Intermediate, 15 min)
Use a SOQL for loop to process 201 tagged Accounts in chunks of 200, and see how the two loop forms behave when a query matches nothing. - Search safely with dynamic SOQL (Intermediate, 18 min)
Fix a dynamic SOQL search so a quote in the input cannot rewrite the query, using queryWithBinds or escapeSingleQuotes. - Filter with date literals (Intermediate, 18 min)
Count tagged Opportunities with SOQL date literals such as NEXT_N_DAYS, LAST_N_DAYS and TODAY instead of computed dates. - Search names with SOSL (Intermediate, 18 min)
Run one SOSL search across Accounts and Contacts, return the names in a set order, and see why tests need Test.setFixedSearchResults.
Change records (DML)
Insert, update, upsert and delete, Database methods, save results, savepoints and bulk-safe DML. 9 drills, intermediate, run with Apex Path.
- SObject field access (Beginner, 8 min)
Set and read Account fields with dot notation and dynamic SObject methods without saving a record. - Insert a tagged Account (Beginner, 10 min)
Insert one uniquely tagged Account and verify the saved record by its assigned Id. - Update a tagged Account (Beginner, 12 min)
Create one tagged Account, update its Description, and read the record before and after to prove the change. - Delete challenge-created records (Intermediate, 12 min)
Delete two tagged Accounts in one statement and prove they moved to the Recycle Bin. - Bulk update once (Intermediate, 15 min)
Prepare three tagged Accounts in a loop and update the collection with one DML statement. - Upsert: insert or update (Intermediate, 15 min)
Upsert one Account twice with Database.upsert and use isCreated() to show the first call inserts and the second updates. - Handle Database.SaveResult (Intermediate, 15 min)
Let one valid insert succeed while you read the status code of one invalid Account. - Savepoint and rollback (Intermediate, 15 min)
Insert a tagged Account after a savepoint, roll back, and show the row is gone while the Id stays on the variable. - Insert an Account with Contacts (Intermediate, 18 min)
Insert a parent Account, then its three Contacts in one list with AccountId set, and prove the link with a query.
Prove it with tests
Apex test classes that fail when the code is wrong: assertions, test data, mocks and callout mocks. 10 drills, intermediate, run with Apex Path.
- Assert service outcomes (Beginner, 10 min)
Write a test helper that fails the test when a service returns the wrong label. - Create isolated test data (Beginner, 12 min)
Insert your own test Account, then read it back from the database by its Id. - Build a test data factory (Intermediate, 12 min)
Build a test data factory that inserts any number of Accounts with one DML statement. - Test success and exceptions (Intermediate, 15 min)
Write verifiers that fail on a wrong amount, a wrong error message or a missing error. - Check partial DML results (Intermediate, 15 min)
Insert a good and a bad Account with partial DML, and write a verifier that fails on wrong SaveResults. - Reset limits around the act (Intermediate, 15 min)
Use Test.startTest and Test.stopTest to count only the queries the code under test runs. - Prove bulk behavior (Intermediate, 15 min)
Write a list verifier that checks the size and every item, and fails on a wrong 200-item list. - Use a fake collaborator (Intermediate, 18 min)
Replace an outside gateway with a test fake that records calls and can fail once on purpose. - Mock an HTTP callout (Intermediate, 16 min)
Implement HttpCalloutMock and register it with Test.setMock so a client can be tested offline. - Share setup and run as another user (Intermediate, 18 min)
Create a fixture user and Account in @TestSetup, then use System.runAs to insert a Task as that user.
Automate with triggers
Before and after triggers, handler classes, bulk patterns, recursion guards and roll-ups. 10 drills, intermediate, run with Apex Path.
- Write your first before-insert trigger (Beginner, 10 min)
Loop over Trigger.new in a before-insert trigger and give blank Contact descriptions a default, with no DML. - Create follow-up Tasks after Contact insert (Intermediate, 15 min)
Build one related Task per new Contact in an after-insert handler and insert them all with one DML statement. - React to a Contact email change (Intermediate, 15 min)
Compare each Contact with its saved version in Trigger.oldMap and mark only records whose Email really changed. - Route Case trigger contexts (Intermediate, 15 min)
Add the update event to a Case trigger and use Trigger.operationType to send inserts and updates to the right handler method. - Mark parent Accounts in bulk (Intermediate, 18 min)
Collect Account Ids from new Contacts in a Set and mark every parent Account with at most one query and one update. - Move Opportunity logic to a handler (Intermediate, 15 min)
Move the logic out of a busy Opportunity trigger into beforeInsert and beforeUpdate handler methods. - Require a Contact email domain (Intermediate, 18 min)
Use a field-level addError in a trigger handler to reject Contact emails outside @example.com, including look-alike domains. - Stop recursive Account updates (Advanced, 20 min)
Stamp each updated Account once from an after-update trigger, and use a static Set of Ids to stop the update from running the trigger again and again. - Block deleting Accounts with open deals (Advanced, 20 min)
Write a before delete trigger that uses Trigger.oldMap and one query to stop Accounts with open Opportunities from being deleted. - Keep a Contact count on Account (Advanced, 25 min)
Write a Contact trigger that counts the Contacts on each Account again after every insert, update, delete and undelete, with one aggregate query and one update.
Work in the background
Future methods, Queueable, Batch and Schedulable Apex. 6 drills, advanced, run with Apex Path.
- Complete a Contact review in the future (Intermediate, 15 min)
Turn a working synchronous method into a @future method that takes an Id, queries the Contact and saves the review later. - Carry Account work in a Queueable (Intermediate, 18 min)
Carry Account Ids and a label into a Queueable job, then update every target Account with one DML statement, even for 200 records. - Build a Batch lifecycle (Advanced, 20 min)
Implement start, execute, and finish so a Batch job processes selected Accounts and marks completion. - Keep counts across Batch transactions (Advanced, 25 min)
Add Database.Stateful so counts gathered in execute are still there when finish writes the summary. - Schedule an Account review (Advanced, 22 min)
Register a Schedulable class with a cron string, and keep the record work in execute so it runs only when the job fires. - Make a Queueable retry idempotent (Advanced, 25 min)
Keep every applied operation key on the record so a Queueable retry, even a late one, never applies the same work twice.
Debug it
Fix real production bugs: queries and DML in loops, null checks, recursion, mixed DML, empty tests and batch state. 8 drills, intermediate, run with Apex Path.
- Fix a missing null check (Intermediate, 10 min)
A Lead trigger tidies email addresses, but one blank Email makes the whole insert fail. Read the error and fix it. - Fix a query that finds no rows (Intermediate, 10 min)
A lookup method works until an Account is missing, then throws List has no rows for assignment to SObject. Make it return null instead. - Fix a query inside a loop (Intermediate, 15 min)
A Contact trigger looks up each Account one at a time. It passes every test with 5 records and dies on a 200-record import. Bulkify it. - Fix DML inside a loop (Intermediate, 15 min)
An Opportunity trigger creates a follow-up Task per record with insert inside the loop. It fails at 151 records. Collect first, insert once. - Fix a test that checks nothing (Intermediate, 15 min)
A test class is green, yet a wrong total and a missing error both slip through. Add the asserts that make it fail when the code is wrong. - Stop a trigger calling itself (Advanced, 15 min)
Closing one Case now fails with maximum trigger depth exceeded. The after-update handler updates the Case, which runs the handler again. Break the loop. - Fix a mixed DML error (Advanced, 20 min)
Onboarding creates an Account and a User in one method. In production that throws MIXED_DML_OPERATION. Move the User insert into its own transaction. - Fix a Batch job that forgets its count (Advanced, 20 min)
An archive Batch counts what it changes, but its finish report always says 0. Find out why the counter resets and fix it.
Ship it safely
Governor limits, security and the habits that survive a production deploy. 4 drills, advanced, run with Apex Path.
- Measure query cost (Beginner, 10 min)
Measure how many SOQL queries a loop costs, then compare it with one bulk query. - Build Contacts with one query and one insert (Intermediate, 18 min)
Refactor a loop that queries and inserts once per record into one query, a Map lookup and one insert. - Gate a release with test evidence (Advanced, 18 min)
Choose a safe Salesforce test level and block promotion without clean results and a rollback reference. - Build a secure Account service (Advanced, 25 min)
Deploy a with-sharing Account service that blocks users without access and strips fields they cannot create.