Sep 15, 2015

Stop Drop & Rollback -- How to handle errors properly in Apex/Salesforce with Try, Catch, Rollback Best Practices

Visit our website




Smokey Says - Care will prevent 9 out of 10 Apex errors!
There are some assumptions that we know about when coding in Salesforce in regards to protecting the developer from saving partial-processed data in Salesforce.

- Any exceptions that happen in code(within the same entire execution) will roll back the entire execution so that all database changes leading up to the failure will not be committed to the database.  This is true for triggered code, page controllers, asynchronous code, etc.  This was a GREAT thing as I started developing in Salesforce and really protected the data for the customers.

- The above is only true if: 1. You are not using try/catch blocks around DML operations(which results in undesirable errors on top of standard pages or white-screen error pages from custom pages) 2. Or you are using try/catch blocks and are properly handling the error inside the 'catch'.

There are many uses of try/catch which I won't cover here but you can check that out here.

My Experience

The more custom work I did the more I realized that I was using try/catch all over the place but didn't immediately understand the dangerous implications of using it.  I've talked about this topic many times with other developers, explaining my experience, and it can be a complex one to discuss but I'll try to lay out some hypothetical situations.

Situations

  1. You want to use try/catch in a page controller so that you can show a friendly error on the screen for the user.  There are 2 DML operations in the 'button click' action, and say the error happens on the second DML.  So you tell the user there is an issue by adding a pagemessage.  Did you do anything to undo the 1st DML which was a success? Salesforce will not roll-back that DML for you because you've essentially "caught" the error that would have done that.  Result: Bad data. You need to roll-back.
  2. You've got a trigger on Case that will roll-up some custom information to the Account.  This important logic will keep the data in-check.  But sometimes there is an issue when you run the DML Update to the accounts, so you wrap the DML in a try/catch and perform a system.debug to figure things out a bit better.  You've deployed things like this to production since it is very rare.  By "catching" this error and the only handling you've done is a debug, you have told Salesforce not to roll-back the changes to the original Cases that fired the trigger.  You'll end up with Cases(newer changes) out of sync with Account(failed update) in your database.  The solution is that you need to attach a ".adderror()" to the appropriate Case records in the trigger which will tell salesforce to roll-back changes and will show your custom more descriptive error to the user(or to the calling code / external system). Result: Bad data. You need to .addError

Code Examples - VF Controller


VF Controller Good Example no try/catch - Standard exception handling, SF handles database protection KEY: Do nothing extra (Good)

VF Controller Bad Example with try/catch - Account will insert without the case
Since we are using try/catch below, we are able to show the user a nicer error message with the ApexPages.addMessage method. BUT, we are now taking over the standard salesforce pattern of "error and rollback". And we are forgetting to rollback ourselves.


VF Controller Good Example with try/catch - If Case fails, Account insert will rollback KEY: Using Database Rollback (Best Practice)


Code Examples - Trigger

Trigger Good Example no try/catch - Standard exception handling, SF handles database protection KEY: Do nothing extra (Good)


Trigger Bad example with try/catch - Account update could fail but we didn't stop the Cases from being inserted


Trigger Good example with try/catch - If Account update fails, we flag the Cases with an error which will show to user and prevent the Case inserts KEY: Use .addError() method (Best Practice)

Other Tips

- When using the generic exception class, it will catch any type of error besides for limits and some other situations. Be careful not to put too much inside the same Try block as it'll be more difficult to figure out what caused the error. Best practice would be use specific exception classes like dmlexception and only put a single DML in that try block.

- Make sure to test your catch blocks.  In your manual tests you can configure a dummy "requirement" on an object or a validation rule you know will fail, or leave out a required field. Then attempt the custom button, standard button, whatever it is that starts the code which should fail and see how your catch block operates.  For unit tests this could be more difficult but you could employ some special logic to conditionally fill in data or fail to fill in data in your code.  Say your controller would be looking for "Unit Test *** Fail DML Required Field" it could know to purposely clear a field on a record before the DML which would then fire your catch block.  Or you have a Unique field setup in an object and let your unit test try to insert 2 records with the same values in that field.  There's many other things you could do, up to you.

Dreamforce '15 Session: Enhanced Web Service Testing: A Better Mock Structure

Session Title: Enhanced Web Service Testing: A Better Mock Structure

When: Tuesday, 9/15/15 at 5:30 PM
Where: Moscone West - 2007

Details
Salesforce provides an interface for testing callouts named HttpCalloutMock used to cover remote callouts. While adequate for simple callouts, in the real world you often need something more flexible, as in the case of multiple and varying responses from the same or varying endpoints. More precise testing and coverage can be obtained by extending the standard interface. Join us as we demonstrate a solution to use to enable the flexibility required for complex integration and synchronization apps.

Deck:  http://bit.ly/df15_kirk_deck
Unmanaged Package:  http://bit.ly/df15_kirk_pack_v1_1
Original Blog Post:  http://blog.crmscience.com/2014/12/test-coverage-pattern-for-multi-callout.html

Sep 8, 2015

Lab Coat Challenges: Challenge #5 Now Available


Challenge 5: Magic 8 Ball

Hey developers (and budding developers)!

Dreamforce is next week!  Who saw that coming?  If you follow us on Twitter (@crmscience), you probably did. #CalendarCube.

Being next week, you might be strategizing when you're going to get your selfie in with SaaSy.  With this week's challenge, we wanted to give you a way to wake up in the morning and definitely have an answer for "Will I see SaaSy today?"
 
Objective
You'll be creating a magic 8 ball like method that returns a string.  This magic 8 ball, though, only answers one question... "Will I see SaaSy today?"  You know, because a Dreamforce without a SaaSy selfie is like eating a cupcake without icing.

Within the method, determine how to randomly select return one of the valid answers.

Click here for the full details, rules, and starter code!























Sep 7, 2015

Lab Coat Coat Challenges: Challenge #3 Responses!



Great round of submissions for this Lab Coat Challenge!

It's interesting to see how everyone had a similar approach to resolving the problem and used different tools to get there. Some were able to create concise solutions that made use of system methods while others ended up replicating the same work or something very similar. Great example of where it pays to know (or, minimally know they exist) some of the more rarely used methods in the documentation.

Thanks to everyone who has participated so far and for those that are checking these answers out now and think they can do better!  Keep the submissions coming and we hope you're enjoying these challenges!

Link:  Lab Coat Challenges

Starter Code



Asserts for all code




@PramForce




@melmoussaoui




@DeborahOrth




@DouglasCAyers




@@scottpelak




@MichaelWelburn




@venky_mass




@@SF_Tidbits






@anonymous






@nickforce




@CloudAnatomy




@michaelbsalty













Sep 1, 2015

Lab Coat Challenges: Challenge #4 Now Available

Challenge 4: Get Crackin'

Hey developers (and budding developers)!

Think you're pretty saavy when it comes Salesforce?  Demonstrate your mastery of the platform and earn the best badge possible - an honorary CRM Science lab coat!

The last challenge had you thinking with a particular mindset.  This week is a bit of a continuation of that.

Clue
k == a

Objective
Programmatically determine how k relates to a and use that knowledge to decrypt the following string:

Click here for the string!