Showing posts with label apex. Show all posts
Showing posts with label apex. Show all posts

Wednesday, February 12, 2014

Preventing Your Form from Submitting Twice (or more!)

If you have a visualforce page that does any kind of update or insert and you're using a custom method on your controller, be mindful of your aggressive clickers!  They'll get you in trouble by submitting your form more than once, wreaking havoc on all of your hard work.

I just dealt with this and was surprised that I was not able to use the apex:actionstatus and facet combination that I typically use to disable buttons that initiate queries.  Turns out that the important detail is that the actionstatus tag requires us to rerender a component.  When this rerendering occurs, you potentially get another form submission.  It may have to do with the redirect my page was doing after the save.  The good folks over at the stack exchange saved my bacon once again.  Their approach to disable your button is the following:

Button click calls an actionfunction js.  The actionfunction does some javascript to disable the buttons using jquery, then calls the actionfunction component, which describes the controller method to run and what to do when the method completes.

I changed up the accepted stack exchange solution in two ways:

1. The button, while gray and disabled, still behaved like a button (it was still clickable).  I ended up using jquery to remove the btn class (which makes it clickable) and also restore the btn class, after any page messages/errors are displayed:

function buttonsEnabled(enabled) {
        // to disable the button
        if (enabled === false) {
            var $b = jQuery('.btn');
            $b.removeClass('btn');
            $b.addClass('btnDisabled');
            $b.toggleClass('btnDisabled', true).attr('disabled', 'disabled');
        } else {
            var $b = jQuery('.btnDisabled');
            $b.toggleClass('btnDisabled', false).attr('disabled', null);
            $b.removeClass('btnDisabled');
            $b.addClass('btn');
            
        } 
    }

2. If you have some validations on your page, you'll want to be sure you're re-rendering the messages tag in your action function.  In this example, my pagemessages has the id "msgs".

<apex:form id="form">
<apex:actionFunction name="doSomeWorkActionFunction" 
        action="{!mySave}" 
        oncomplete="buttonsEnabled(true);"
        rerender="msgs">
</apex:actionFunction>
<apex:pagemessages escape="false" id="msgs"/>
<apex:pageblock mode="Detail" id="pageblock" >
    <apex:pageblockButtons >
        <apex:commandButton id="mysavebutton" 
                  onclick="return doSomeWork();" value="Save"  />
        <apex:commandButton action="{!myCancel}" value="Cancel" immediate="true" />
    </apex:pageblockButtons>

Monday, December 16, 2013

Deep Thoughts on Apex Test Methods

You're good enough.
You're smart enough.
You can write a good apex test method.

I just completed a major rewrite of all test methods for a client and while it was painful at times, I think it puts them in a position to extract some value from what was previously just a production deployment hurdle. Trust me, I'm not yet a full test-driven-development convert but I do believe that you can help your business automate some testing, and maybe even save some cash, if you take the time to think about your testing and apply it to your test methods.

You get better at the things you do over and over and writing good test methods is certainly something you can expect to have plenty of opportunity to practice.  There are some great resources out there to be sure you are repeating good habits. I'd start with Dan Appleman's Advanced Apex book as he has some great ideas for test class writing. Some other articles that I think are helpful and instructive are:

http://jessealtman.com/2013/09/proper-unit-test-structure-in-apex
http://wiki.developerforce.com/page/How_to_Write_Good_Unit_Tests

With this recent rewrite effort, some of the good practices I've incorporated are:

  • Moving test methods from functional classes into separate Test Classes
    • This allows you to decouple your tests from your functional classes, which excludes your tests' ability to reach private methods.  This will give you some flexibility with refactoring your code without being tied down by your test methods.
  • Asserting results
    • While you may achieve the 75% goal of code coverage, your tests will have little/no value if you are not actually checking expected versus actual results.  As a managed package developer, you'll also get flagged during the security review if you are not asserting your results. 
  • Centralizing and standardizing helper utilities
    • Like other apex you write, you should try to encapsulate where you can and use helpers to minimize the effort in testing various permutations of data against your code.
  • Testing negative scenarios
    • This is another area where you can get some value out of unit testing.  While it may take 80% of your effort to identify and code these, it's going to yield lots of value in improving your code and confidence in your code handling atypical scenarios.
  • Testing as an end user
    • Unfortunately as developers, we are system admins and almost everything we test works as expected because we don't have to deal with sharing or role hierarchy or object/field visibility. However, in the real world, our users are almost never system admins, so testing as a system admin makes no sense.

This is certainly not the complete list of best practices, but it's a good start.

As with other things in Salesforce, there is some room for improvement with the execution of unit testing in the application. In particular, I'm still frustrated by what Jeff Douglas calls the "black art".  Like Jeff, I've come across some peculiar behaviors that can be maddening.  For example, if you are testing a trigger and need to create a user and then test the dml, there's a pretty good chance you're going to get a mixed DML exception.  What is maddening, however, is that the error will not be caught in the force.com ide.  Oh, and you can deploy to production with this too!  The only thing keeping me sane was knowing that someone else noticed this too:



And aside from the nonsense of trying to estimate your code coverage in any of the tools out there, it would be nice if the test classes had the code coverage estimation like the functional classes for those of us separating them:


And don't get me started on the new test execution screens.  Aside from queuing your tests, they provide no value!

I think there is plenty to like about putting some effort into doing proper unit testing in Salesforce.  I'm sure if you build in the time to your sprints/plans, it will pay dividends in the long term.  Just be sure you approach this with a good sense of humor.


Wednesday, November 13, 2013

A Bug!

It's not often that I come across a real bug with Salesforce's apex or visualforce platform.  Most often there are limitations or shortcomings that you have to workaround.  Recently, I had to make an urgent change to a trigger and it's associated test class.  However, when I attempted to comment out a line in my test class, I got the following error in the editor when I tried to save:

java.lang.reflect.InvocationTargetException

When I attempted to make the same change in the developer console, I got another error:

 An unexpected error has occurred. 421011484-16071 (1420197083) for deploymentId=1drJ00000002FDxIAM If this persists, please contact customer support.


Fortunately, I was able to still deploy my code without the test class change but I opened a case anyway and after waiting a few days for a reply, was told that it was a known issue.  The instructions from developer support were:
  • Please clear Test results and try to save the code:
    • From Setup, click Develop | Apex Test Execution |View Test History | Clear test results. 

However, even before I did these actions, I tried to update the test class again and surprise!  no errors. So, something fishy is going on... Support wants to close the case but I'm inquiring for additional details.  Will keep you posted.


**Update Nov 13**

Salesforce has responded and indicated that it was a bug but has been fixed.  Details here: https://success.salesforce.com/issues_view?id=a1p30000000T17j

Monday, January 9, 2012

Spring 2012

I was thumbing through the Spring 2012 release and for the first time since Chatter was released, I was impressed with the haul of changes.




Here are some highlights coming in early February to a Salesforce instance near you:

Salesforce for Outlook

  • We use a Outlook plug-in that synchronizes Salesforce contacts, events, etc with Salesforce but it is neither Salesforce for Outlook nor Connect for Outlook.  The primary driver for going with a 3rd party's solution was the inability of Salesforce for Outlook users to discriminate between private and public Contacts and tasks/meetings.  Salesforce has finally addressed this shortcoming, over 2 years since the Salesforce for Outlook plug in was released.

Workflows

  • We'll have to see exactly how this works and the limitations around it but one of the potentially more significant changes coming in Spring 12 is the cross-object workflow.  Hopefully, we can avoid the need to write triggers for basic field updates.  Again, this is one that has been in the pipeline for years and glad to see Salesforce finally address it.

Reports

  • It's hilarious to me that Salesforce no longer calls these "reports" but rather has latched onto the buzz around "Analytics".  Whatever it is, they've improved the service markedly.  The most notable improvements to me are the following:
    • You can now run an exception report like "Account without Opportunities".  To do this previously, you almost certainly had to export the results of two different reports into Access, Excel, or SQL Server.  You'll see this tagged in the documentation under "Cross Filters".
    •  The other significant improvement should now allow us to create reports like Opportunities with Activities and Contacts.  Previously, you could have Parent -> Child -> Grandchild, but not Parent -> Child -> Sibling.  Again, we'll see how this actually looks soon, but this is potentially a big improvement.

Apex

  • A couple notable changes for developers to keep an eye on:
    • The number of schedule Apex jobs has been increased from 10 to 25.
    • Test methods can no longer use existing customer data (with the exception of user/profile and record type).  This means all test methods will now need to create new data.  It looks like they've created an exception but for those of us w/ existing non-compliant code, we'll have to do some retrofitting with the "SeeAllData" property.