Here is something that catches a lot of new Salesforce developers off guard.
You can write the most elegant, well-structured Apex trigger in the world. It can handle bulk data perfectly. It can process edge cases that no one even thought of. But none of that matters if you cannot get it into production.
And Salesforce will not let you deploy a single line of Apex to production unless your test classes meet a very specific bar.
“According to Salesforce's own fiscal year 2025 earnings release, the company now serves over 150,000 customers, with roughly 90% of Fortune 500 companies running their operations on the platform. That is a massive amount of custom code sitting in production orgs around the world. And every single piece of it had to pass Salesforce's testing requirements before it got there”.
IDC's 2025 Worldwide Semiannual Software Tracker has ranked Salesforce as the #1 CRM provider for the 12th consecutive year, thereby holding a 20.7% global market share. Also, the platform has generated $37.9 billion in revenue for fiscal year 2025, according to their official investor relations page.
Additionally, their SEC filing for Q3 fiscal 2026 raised the complete year revenue guidance to almost $41.45 billion. In a separate SEC filing from October 2025, Salesforce also announced a long-term revenue target of $60 billion, by fiscal 2030.
Table of Contents
The quality of code and testing is crucial in Apex programming. The deployment fails for many reasons, including:
The production deployment may fail at any time if you don't complete a test, even for a trigger.
This tutorial covers Apex Testing and Deployment, including the fundamentals of Salesforce testing, how to write test classes, code coverage percentages, and more.
Let's get into it.
In most programming environments, testing is a best practice. In Salesforce, it is a hard requirement. You literally cannot skip it. The platform will block your deployment if the tests do not meet the threshold.
There are a couple of reasons Salesforce takes this approach.
Multi-tenant architecture: Your org shares server resources with thousands of other orgs. Salesforce needs to ensure that the code running on their infrastructure actually works before it goes live. Broken code in production does not just affect your org. It can impact performance for everyone on the same server pod.
Data integrity: Salesforce orgs store business critical data like financial records, customer information, healthcare data, legal documents. Untested code that modifies this data can cause serious damage that is extremely expensive to undo.
AppExchange distribution: Let’s say you are building a managed package for the AppExchange. According to Salesforce, Apex code in the package should have a minimum of 75% code coverage. Without that, the security review will not even begin.
The source material from MindMajix describes two fundamental approaches to testing in Salesforce.
Well, before any Apex code is deployed to production or distributed through the AppExchange, these conditions need to be satisfied.
For structured, hands-on training that walks you through all of this with real projects, MindMajix's Salesforce training program covers testing, code coverage strategies, and production deployment in depth.
Think of a test class as a separate Apex class that has one job and one job only. It tests your actual production code. It does not do anything for the business. No records get created for real users. No emails go out and no workflows fire.
Let us see the example from the source material. We have a custom object called Levis__c (think of it as a jeans inventory tracker), an Apex class that processes records, and a trigger that fires on insert.
The Apex Class:
public class JeanClassDemonstartion {
public static void processJeans(List<Levis__c> jeansList) {
for (Levis__c jean : jeansList) {
if (jean.Price__c >= 1000) {
jean.Price__c = jean.Price__c - 100;
}
}
}
}
This class takes a list of jeans records, checks if the price is 1000 or above, and applies a 100 rupee discount.
trigger JeanTrigger on Levis__c (before insert) {
JeanClassDemonstartion.processJeans(Trigger.new);
}
Fires before insert and passes the incoming records to the class for processing.
The Test Class:
@isTest
public class JeanClassDemonstartionTest {
static testMethod void testDiscountApplied() {
// Create a record with price above 1000
Levis__c j = new Levis__c();
j.Name = 'Louis';
j.Price__c = 1200;
Test.startTest();
insert j;
Test.stopTest();
// Retrieve the record from the database
Levis__c result = [SELECT Price__c FROM Levis__c WHERE Id = :j.Id];
// Verify the discount was applied
System.assertEquals(1100, result.Price__c, 'Price should be reduced by 100');
}
static testMethod void testNoDiscountBelowThreshold() {
// Create a record with price below 1000
Levis__c j = new Levis__c();
j.Name = 'BasicJean';
j.Price__c = 500;
Test.startTest();
insert j;
Test.stopTest();
Levis__c result = [SELECT Price__c FROM Levis__c WHERE Id = :j.Id];
// Price should remain unchanged
System.assertEquals(500, result.Price__c, 'Price below 1000 should not change');
}
static testMethod void testExactThreshold() {
// Create a record with price exactly at 1000
Levis__c j = new Levis__c();
j.Name = 'ThresholdJean';
j.Price__c = 1000;
Test.startTest();
insert j;
Test.stopTest();
Levis__c result = [SELECT Price__c FROM Levis__c WHERE Id = :j.Id];
// 1000 meets the >= condition, so discount applies
System.assertEquals(900, result.Price__c, 'Price at 1000 should get discount');
}
}
Let’s break it down because there are several important things going on.
The source material on deployment mentions something that a lot of people miss. Test classes annotated with @isTest no longer have to be private. You can create public test utility classes that expose common methods for test data creation.
@isTest
public class TestUtil {
public static Levis__c createJean(String name, Decimal price) {
Levis__c j = new Levis__c();
j.Name = name;
j.Price__c = price;
insert j;
return j;
}
public static List<Levis__c> createBulkJeans(Integer count, Decimal price) {
List<Levis__c> jeans = new List<Levis__c>();
for (Integer i = 0; i < count; i++) {
jeans.add(new Levis__c(Name = 'Jean' + i, Price__c = price));
}
insert jeans;
return jeans;
}
}
Test methods can call these public methods in other test classes. This restricts you from duplicating data creation logic across other test classes. This pattern saves a great deal of time on projects with 50 or more test classes.
Salesforce gives you several options.
This is the part that determines whether your deployment succeeds or fails. Code coverage refers to a percentage that tells you how much of your Apex code was actually executed by your test methods.
The formula is simple.
Code Coverage = (Lines Executed by Tests / Total Executable Lines) × 100
Salesforce highlights your code in two colors after a test run.
Lines that do not count toward the total include comments, blank lines, System.debug() statements, test class code, and curly braces on their own lines.
Salesforce requires an org wide average of at least 75% code coverage to deploy to production. This is not per class. It is the average across every Apex class and trigger in the deployment package.
Let us see how this works with a real example from the source material.
Scenario 1 (Fails):
TestClass1 covers ApexClass1 at 75%
TestClass2 covers ApexClass2 at 50%
TestClass3 covers ApexClass3 at 80%
Average = (75 + 50 + 80) / 3 = 68%
68% is below 75%. Deployment blocked. None of these classes go to production.
Scenario 2 (Passes):
TestClass1 covers ApexClass1 at 90%
TestClass2 covers ApexClass2 at 50%
TestClass3 covers ApexClass3 at 90%
Average = (90 + 50 + 90) / 3 = 76%
76% clears the 75% threshold. All three classes deploy successfully.
Notice something important here?
In Scenario 2, ApexClass2 only has 50% coverage on its own. That is technically fine because Salesforce takes the average of the entire package.
But this is a risky approach. One badly covered class can drag down the average, and adding new classes later can push you back below 75% unexpectedly.
Every trigger in the deployment package must have some coverage. Even 1% is technically enough per trigger.
But here is the catch. If a trigger has 0% coverage, meaning no test method exercises it at all, the deployment will be rejected regardless of the org wide average.
1. Through Setup:
Go to Setup, then navigate to Apex Classes. You will see a column showing the percentage of Apex used. The source material references the path:
Login to Salesforce Org → Setup → Build → Develop → APEX Class → Percent of Apex Used.
2. Through Developer Console:
After running tests, open the class or trigger you want to inspect. Next, move to the Code Coverage dropdown and select the test class. Here, the editor will highlight covered lines in blue and uncovered lines in red.
You will find situations where coverage was sitting at 72% the night before a production deployment. Here is what actually works.
@isTest
static void testBulkInsert() {
List<Levis__c> jeans = new List<Levis__c>();
for (Integer i = 0; i < 200; i++) {
jeans.add(new Levis__c(
Name = 'BulkJean' + i,
Price__c = 1500
));
}
Test.startTest();
insert jeans;
Test.stopTest();
List<Levis__c> results = [SELECT Price__c FROM Levis__c WHERE Price__c = 1400];
System.assertEquals(200, results.size(), 'All 200 records should have discount applied');
}
Assertions are basically how you prove your code actually did the right thing. See, here is what a lot of people do not realize.
A test method can pass with flying colors even if the logic it covers is completely busted. Salesforce does not care what your code produced. It only cares that the method runs without crashing. That is it. So unless you add assertions, a "passing" test is really just telling you "nothing exploded." It is not telling you "the output was correct.”
There are three of these in the System class, and honestly you will use the first one about 90% of the time.
1. System.assertEquals(expected, actual, message) is the one you reach for most. You give it the value you expect, the value your code actually produced, and a message. If those two values do not match, the test dies right there.
Integer expected = 1100;
Integer actual = result.Price__c.intValue();
System.assertEquals(expected, actual, 'Discount should reduce price to 1100');
2. System.assertNotEquals(val1, val2, message) is the reverse. It fails when the two values ARE the same. You can use this one when you need to confirm that a record was actually created and got an ID assigned.
System.assertNotEquals(null, result.Id, 'Record should have been inserted with an ID');
3. System.assert(condition, message) is the simplest one. You pass it a true/false expression. False means failure.
System.assert(result.Price__c < 1200, 'Price should have been reduced');
So let us say you have five test methods in a class and the assertion in method number two fails. Method two stops executing right at that line. Whatever comes after it in that method never runs.
But methods three, four, and five? Those still run like nothing happened. Salesforce isolates the failure to the method where it occurred, which is nice because you still get results from everything else.
Writing Meaningful Assertion Messages
This is a small thing that makes a big difference when you are debugging a failed deployment at midnight.
Bad:
System.assertEquals(1100, result.Price__c, 'Test failed');
Good:
System.assertEquals(1100, result.Price__c,
'Expected price of 1100 after discount on jean: ' + result.Name
+ '. Actual price: ' + result.Price__c);
The second version tells you exactly what went wrong without you having to dig through the code. When you have 30 test methods and one of them fails, descriptive messages save a lot of time.
Everything we have covered so far leads to this point. You have written your Apex code. You have created test classes. Your coverage is above 75%. Your assertions all pass. Now you need to actually get it into production.
This is a fundamental rule of the platform. Salesforce will not let you write or edit Apex code in a production org. So, you will have to build everything in a sandbox, test it there, and then push it to production through a deployment.
That is not an accident either. Salesforce set it up that way because they want your code tested before it gets anywhere near real users and real data. No shortcuts.
When you hit deploy, Salesforce does not just take your word for it that everything works. It runs its own checks first.
The first thing it looks at is compilation. Every class and trigger in your package needs to be compiled cleanly. One syntax error in any file and the whole thing gets sent back.
Syntax errors block everything.
sfdx force:source:deploy -p force-app -l RunLocalTests -u MyProductionOrg
The source material on deployment mentions two API objects that are specifically designed for CI integration.
Additionally, the System class provides methods for determining the current execution context.
System.isBatch() // true if running inside a batch job
System.isFuture() // true if running inside a @future method
System.isScheduled() // true if running inside a scheduled job
These are useful when your code needs to behave differently depending on the execution context, and your test methods need to account for that.
Let’s walk through the entire flow one more time, from start to finish and connect each piece.
Step 1: Write your Apex class or trigger in a sandbox. Build the business logic. Make it bulkified. Handle edge cases.
Step 2: Write your test class. Create test data inside each method. Use Test.startTest() and Test.stopTest() to get fresh governor limits. Add assertions for every scenario.
Step 3: Run the tests. Use the Developer Console or Apex Test Execution page. Check that every method passes. Look at the code coverage percentage.
Step 4: Verify coverage. Open your Apex class and trigger. Check the highlighted lines. Blue means covered. Red means not covered. If you are below 75%, add more test methods that exercise the uncovered lines.
Step 5: Deploy. Use a change set, the Salesforce CLI, or VS Code. Salesforce will re run all tests in the production org. If everything passes and coverage is above 75%, the deployment succeeds.
Step 6: Verify in production. Spot check the functionality. Confirm that the trigger fires correctly with live data. Monitor the debug logs for any unexpected behavior.
That is the complete picture of Apex Testing and Deployment in Salesforce.
Testing fundamentals: Salesforce requires all Apex code to be tested before deployment. There are no exceptions to this rule.
Test classes: Annotated with @isTest, containing test methods that create data, execute code, and verify results through assertions. Public test utility classes help share common data creation logic.
Code coverage: The percentage of executable lines your tests actually run. The org wide average must be at least 75%. Salesforce calculates this by averaging coverage across all classes and triggers in the deployment package.
Assertions: The three methods (assertEquals, assertNotEquals, assert) that verify your code produced the correct output. Without assertions, passing tests proves nothing.
Deployment: All development happens in sandboxes. Salesforce re-runs every test during deployment and blocks anything that falls below the coverage threshold or has a failing test.
Every one of these pieces is connected. You cannot deploy without tests. Tests are meaningless without assertions. Assertions are useless without proper code coverage. And coverage does not count if the deployment process catches a failure.
If you have been reading this tutorial and thinking, "I get the concepts, but I want someone to walk me through this on a real project," that is exactly what MindMajix built their Salesforce training program around.
Test class design, coverage optimization, assertion strategies, CI/CD pipelines for Salesforce, all of it. And they did not just throw a curriculum together from old documentation.
The whole thing is structured around what companies are actually asking in interviews right now and what real projects demand from day one.
What caught my attention is the placement track record. Over the last couple of years, a significant number of candidates who went through this program ended up getting placed in Bangalore, Hyderabad, Gurgaon, Pune, and Noida. Those are not small job markets.
The competition there is intense, and the fact that MindMajix-trained candidates are consistently getting through tells you something about the quality of preparation they are getting.
1. What is the minimum code coverage required for production deployment?
Salesforce requires an average of at least 75% code coverage across all Apex classes and triggers. This is not per class. It is the average of all classes in the deployment package. If one class has 50% but others compensate, the deployment can still work out as long as the average is 75% or more.
2. Can I write Apex code directly in production?
No. Salesforce does not allow you to create or modify Apex code in production environments. All development must happen in a sandbox. You then deploy the tested code to production through change sets, the Salesforce CLI, or the Metadata API.
3. What happens if one test method fails during deployment?
The entire deployment is blocked. Salesforce runs all tests in the org during a production deployment. Even if a single method fails, nothing gets deployed. You need to fix the failing test and try again.
4. Do System.debug() statements count toward code coverage?
No. Salesforce explicitly excludes System.debug() statements from the coverage calculation. Adding debug statements will not improve your coverage percentage.
5. What is the purpose of Test.startTest() and Test.stopTest()?
These methods create a separate governor limit context. Everything between them gets a fresh set of limits. This is especially useful when your test setup consumes DML or SOQL, because the actual code under test gets its own limit allocation.
6. Where can I find more resources on Salesforce testing and deployment?
MindMajix offers several free and paid resources.

Our work-support plans provide precise options as per your project tasks. Whether you are a newbie or an experienced professional seeking assistance in completing project tasks, we are here with the following plans to meet your custom needs:
| Name | Dates | |
|---|---|---|
| Salesforce Training | May 12 to May 27 | View Details |
| Salesforce Training | May 16 to May 31 | View Details |
| Salesforce Training | May 19 to Jun 03 | View Details |
| Salesforce Training | May 23 to Jun 07 | View Details |