Showing posts with label Apex. Show all posts
Showing posts with label Apex. Show all posts

Monday, April 2, 2012

SObject Utility Class Template

Have you ever had to get the ID of a record type for a specific object? Or have you needed to hard-code a particular picklist value into a Visualforce controller/extension or other Apex class? I've had to do both on a fairly regular basis, and it soon became apparent that stress-related health problems may arise if I ever have to refactor my code, or if a user requests a change in picklist values.

To address this issue, it seems that setting up a global constant accessible to all Apex classes would be hugely valuable in making sure that picklist values and record types (and other items) are consistently referenced in Apex.

For example, if I'm trying to set the Status of a Contract to "Activated", I could write the code as follows:
myContract.Status = 'Activated';

This is great if it's the only place I ever deal with the Contract Status field. But what're the chances of that? Instead, how about writing the code as follows?
myContract.Status = ContractUtil.ACTIVATED_STATUS;

Now I don't need to worry about all the myriad places where I've hardcoded the status value. If I ever need to change the status, I can update the constant in ContractUtil or look for all references to ContractUtil.ACTIVATED_STATUS.

Here's the sample code for a generic SObject utility class:
/**
 * Utility class with supporting methods for
 * a Salesforce SObject.
 *
 * Examples of supporting methods include getting
 * a Record Type ID, getting an expected picklist
 * value for a particular field, and conversion
 * to/from other objects.
 */
public class GenericSObjectUtil {

    /**
     * The String value that
     * represents an activated status, which goes
     * into the Status field.
     */
    public static final String ACTIVATED_STATUS =
            'Activated';

    /**
     * The expected default Record Type Name
     * for all users.
     */
    public static final String DEFAULT_RECORD_TYPE_NAME =
            'This SObject';

    /**
     * The String value that
     * represents a draft status, which goes
     * into the Status field.
     */
    public static final String DRAFT_STATUS =
            'Draft';

    /**
     * The map of RecordTypeInfo
     * objects retrieved by describing the
     * SObject, keyed by the Record Type Name.
     * 
     * This is stored to make
     * getRecordTypeId method calls
     * more efficient.
     */
    private static final Map recordTypeInfosByName =
            Schema.SObjectType.Contract.getRecordTypeInfosByName();

    /**
     * Retrieve the Record Type ID based on a
     * given Record Type Name.  If no match is
     * found, then return null.
     *
     * @param  name The name of the Record Type
     * @return      The ID of the Record Type,
     *              if found; null otherwise.
     */
    public static Id getRecordTypeId(String name) {
        return recordTypeInfosByName.get(name).getRecordTypeId();
    }   // public static Id getRecordTypeId(String)
}   // public class GenericSObjectUtil

Note: I know the above code may be cut off a bit due to width limits in Blogger, but you should be able to copy and paste the code into a text editor to see the full structure if necessary.

What do you think? Feedback on this implementation or other solutions will be appreciated!

Tuesday, March 20, 2012

Test Class Template for Apex Triggers

Having written a lot of test methods to validate trigger functionality, I've developed a general template for writing trigger-related test methods that hopefully encompasses all of the steps needed. I believe that there must be a generic template that can be applied to 99% of the trigger test cases out there, and I hope this is a good start in that direction.

@isTest
private class GenericTriggerTest {

    /**
     * Plain English description of what is being
     * tested and why.
     */
    public static testMethod insertSObjects() {

        // Stage the test data.

        // Set parameters and expectations.
        // Which records am I using?
        // What do I expect to change or see
        // at the end of the test?

        // Validate pre-conditions.
        // Impersonate another user
        // if appropriate.

        //System.runAs(null);
        System.assert(false, 'TODO');

        // Start the test.

        Test.startTest();

        // Execute test operations.

        // Stop the test.

        Test.stopTest();

        // Validate the results.

        System.assert(false, 'TODO');
    }   // static insertSObjects()

    /**
     * Plain English description of what is being
     * tested and why.
     */
    public static testMethod updateSObjects() {
        System.assert(false, 'TODO');
    }   // static updateSObjects()

    /**
     * Plain English description of what is being
     * tested and why.
     */
    public static testMethod deleteSObjects() {
        System.assert(false, 'TODO');
    }   // static deleteSObjects()
}   // private class GenericTriggerTest

Feedback on the template will be much appreciated.

Wednesday, January 25, 2012

Simple Method to Identify Blank Fields in Visualforce

I've often wondered why there is no ISBLANK() equivalent in Apex for developers to use when validating a Visualforce page. Maybe Salesforce always intended for developers to use the required attribute for the standard Visualforce components.

The problem with relying on field settings, Visualforce required attributes or object validation rules is that there is no consistent presentation of the error messages. For different sites, especially public ones, sometimes the validation errors need to be tailored specifically for the site.

To help this process along, I modularized the concept of ISBLANK() in the simple instance method below.
/**
 * Determine whether a given object is blank
 * or not, in the same manner as the ISBLANK()
 * Visualforce function.
 *
 * @param  o The object to examine
 * @return   Whether the object is blank
 */
public Boolean isBlank(Object o) {
    return o == null;
}   // public Boolean isBlank(Object o)

With this simple method (or variations thereof), developers can iterate through a List<Schema.SObjectField> of required fields and use controller.isBlank(Object) to validate the fields.
for (Schema.SObjectField field : requiredFields) {

    if (isBlank(application.get(field))) {
        addRequiredFieldError(field);
        isValid = false;
    }   // if (isBlank(application.get(field)))
    
}   // for each Schema.SObjectField in requiredFields

Thursday, January 5, 2012

NO-SOFTWARE Salesforce Tutorial: Internal Web Service Callouts

Another developer recently talked about a situation where an Apex web service needed to be exposed to internal, authenticated users in order to work around a DML limitation.

The concept behind the workaround was very intriguing, and after playing around with some Apex code I came up with a tutorial that I hope others may find useful: NO-SOFTWARE Salesforce Tutorial: Internal Web Service Callouts

The key concepts demonstrated in the tutorial include:
  • Generating a WSDL from a global Apex class
  • Generating Apex from a WSDL created from a global Apex class
  • Using the UserInfo.getSessionId method to make callouts to internal web services as an authenticated user

Thursday, December 29, 2011

Last Modified Date Rounding to Whole Seconds

The DateTime class in Salesforce has precision down to the order of milliseconds, as implied by the getTime method which "returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this DateTime object." Since the Last Modified Date field is a Date/Time field, one would assume that the Last Modified Date includes the millisecond value when stamping a record, right? Alas, this is not the case.

A video demonstrating the behavior: http://www.youtube.com/watch?v=IyRd1woiS24

It seems that Salesforce only includes the year, month, day, hour, minute and second when stamping the Last Modified Date. The millisecond value is simply zeroed out without any rounding. So, if a record was actually modified at 12/29/2011 12:31:20.578, Salesforce will stamp the record as being modified at 12/29/2011 12:31:20.000.

Okay, so there's a tiny discrepancy. I mean, we're talking tiny differences here. The million-dollar question: Why do I care?

I care because this little quirk caused my unit test to fail over 80% of the time due to the fact that comparing the Last Modified Date to DateTime.now() was producing unexpected results. For example:
  • At the beginning of the test method, DateTime.now() returned 12/29/2011 4:00:01.500.
  • At the end of the test method, the updated record's Last Modified Date shows 12/29/2011 4:00:01.000.
  • System.assert(record.LastModifiedDate >= testStartDateTime) fails unexpectedly and inconsistently.

The oddity with Last Modified Date is not an impossible problem to work around, but should I really be working around it? What does this mean for test-driven development that Apex is supposed to enable?

Thursday, December 22, 2011

Org ID Automatically Replaced in Sandboxes

I discovered something that made my blood run cold today: The OrganizationInfo.isProduction method I was relying on in Apex to communicate to the correct web service endpoint was returning true in my sandbox orgs.

My OrganizationInfo class is super simple, created as suggested in a comment on the IdeaExchange (Determine sandbox name vs production from apex). Shown below for reference:

public class OrganizationInfo {
    
    /**
     * The Organization ID of the production org.
     */    
    private static final Id PRODUCTION_ID =
            '00DA0000000Kb9R';
    
    /**
     * Determine whether the current org is a
     * production org or a sandbox org,
     * based on whether the Org ID matches the
     * production App org's Org ID.
     *
     * @return Whether the org is Production
     */
    public static Boolean isProduction() {
        return UserInfo.getOrganizationId()
                == PRODUCTION_ID;
    }   // public static Boolean isProduction()
    
}   // public class OrganizationInfo

The obvious question: How does something so simple fail in a sandbox org?

The surprising answer: When a sandbox is created or refreshed, Salesforce automatically does a search and replace and replaces the production org ID with the sandbox org ID. Once a sandbox is created or refreshed, a single line of code changes in the above class:

    private static final Id PRODUCTION_ID =
            '00DZ000000056dv';

This tiny, almost unnoticeable change has been screwing everything up for a long time, and its discovery also explains why we would periodically get strange data in production and also strange responses in our sandboxes.

I wish I had known about this earlier, but who would've guessed? Anyhow, the fix that I've implemented (and confirmed by creating a new sandbox) is to split the ID into 3 parts when assigning it to the constant.

    private static final Id PRODUCTION_ID =
            '00DA0' + '00000' + '0Kb9R';

Amazing... the things one discovers in the worst possible ways...

Friday, November 25, 2011

Using IsPersonAccount Field in Account Triggers

Great news! It looks like the IsPersonAccount field is always available for use in Apex triggers, without the need to retrieve the field values using SOQL!

The following test was used to reach this conclusion (in Winter '12):
  1. Create a trigger that activates or fires before/after insert, before/after update and before/after delete.
  2. Add code to the trigger to check every record in Trigger.new and in Trigger.old with the following assertion: System.assert(accountInTrigger.get('ispersonaccount') != null);
  3. Create a test method to insert, update and then delete an Account.
  4. Verify that the test passes.

Optionally, one can also use the following code in the trigger to take a deeper look at the basic information that's always available, depending on what caused the trigger to fire:
System.debug('Trigger.new = ' + Trigger.new);
System.debug('Trigger.old = ' + Trigger.old);

Thursday, November 17, 2011

Revolutionizing Unit Tests in Salesforce with Public Test Classes

One of the coolest features in Winter '12 for any organization that uses Apex has to be public test classes.

Why? One main reason: Public test classes combined with a tool like HardBoil will revolutionize unit testing in Salesforce!

Imagine a world where functional users (yes, functional users!), not developers, actually setup all of the unit tests through an interactive process that...
  • Saves developers countless hours of writing boilerplate code.
  • Drastically reduces human error in staging test data, checking pre-conditions and asserting post-conditions.
  • Produces clean, self-documenting and easy-to-read test code.
  • And trains functional users to learn the data structures and respect data quality, all at the same time!

Here's the immediately achievable concept: Create a single, public test class (perhaps using HardBoil) called something like EnterpriseTestDb. Then, all one has to do is start every test method by instantiating EnterpriseTestDb as follows:
// Stage test data.

EnterpriseTestDb db = new EnterpriseTestDb();

// Set test parameters.

Account largeAccount =
        db.getAccountByName('Acme Corporation');

Contact mainContact =
        db.getContactByName('John Doe');

...

The best parts about all of this, again, is that...
  • Not a single second of developer time was needed to create potentially hundreds of test records that can support every single unit test in the entire org!
  • Functional users learned the data structure by setting up all of the test data for their developers!

And the long-term concept? Automatically generating test methods already setup to check pre-conditions and assert post-conditions, thereby allowing developers to simply fill in the code in each test method to move from the pre-condition to the post-condition.

In short, I can't wait for this new world to arrive.

Monday, November 7, 2011

assertEquals Method for Multi-Select Picklist Values or Other Delimited Values

I couldn't find a way to easily assert that two multi-select picklist values are identical, especially considering that 'value1;value2' really should be considered equal to 'value2;value1'.

To fix this, I wrote an auxiliary class System2 that has a custom assertEquals method that asserts two Strings contain the same delimited values.

The code is verified by checking the following cases in an associated test class:

s1s2result
pass
afail
afail
aapass
aa;bfail
a;bafail
a;ba;bpass
b;aa;bpass
a;bb;apass
a;b;cc;a;bpass
b;a;ca;b;cpass
a;b;ca;b;c;dfail
a;b;c;da;b;cfail

Monday, October 24, 2011

Project HardBoil: Day 4.5

Succeess! Without registering the app with Google, HardBoil now works with any Google Account using the AuthSub library for Google Data.

Basically, a user can login, pick a spreadsheet and boil the spreadsheet into code right on the spot. With the right amount of documentation and UI cleanup, I think this could be available for beta testing soon...

Sunday, October 23, 2011

Project HardBoil: Day 4

What a milestone! 842 lines of Apex code generated to create test records for 8 different objects that have relationships between each other.

The back-end code definitely should be cleaned up and re-thought a little bit, but the concept is proved and now the only thing I need to make this available to the world for "beta testing" is to setup the Google authentication scheme.

I really wish that Google would simply make Spreadsheets accessible without having to login. Wouldn't that be so much easier?

Project HardBoil: Day 3

I've finally gotten my PHP code to start boiling Apex. Simply enough, the code it generated right now just looks like:

private class TestRecordSet {
    private Map<Id, Compensation_Rate__c> compensationRateMap;
    private Map<Id, Account> accountMap;
    private Map<Id, License_Certification__c> licenseCertificationMap;
    private Map<Id, Term__c> termMap;
    private Map<Id, Part_of_Term__c> partofTermMap;
    private Map<Id, Part_of_Term_Paydate__c> partofTermPaydateMap;
    private Map<Id, Course__c> courseMap;
    private Map<Id, Class__c> classMap;
}   // private class TestRecordSet

Every related SObject class used by HardBoiler will need to have methods that generate the appropriate code for the appropriate location. For example: SObject instances have a boilApexVariableCode method, a boilApexConstructorCode method and a boilApexGetterCode method.

Friday, October 21, 2011

Project HardBoil: Day 2

As I began writing my code today, I realized that there was a problem with the way I designed the related objects.

My improved scheme includes the following:
  • SObject
  • SObjectField
  • SObjectRecord
  • SObjectRecordFieldValue

I also took the easy way out with getting the spreadsheet to be read in the way I expected by duplicating the field name row and adding two more "column header rows" to represent the SOAPType and whether the field is a custom external ID or not.

Project HardBoil: Kickoff

So, I've decided to start a project called HardBoil, for lack of a wittier name. What's the goal of this project? To produce a web app that generates boilerplate code for Apex test classes. Specifically, it generates boilerplate code for Apex test classes to create test data to be used in individual unit tests.

What might that code look like? Take a look at the inner TestRecordSet class in this example test class. Some basic elements of the boilerplate code include...
  • List of test records.
  • Map of test records by ID.
  • Map of test records by record name.
  • Constructor that creates the test records and populates the maps.
  • Standard getter methods to get test records by index, ID or record name.

Concept

  1. Create a Google spreadsheet containing test records for different objects, with each object on a separate worksheet.
  2. Login to HardBoiled, point to the spreadsheet, and click a button to generate hundreds of reliable lines of code to setup records for staging best-in-class unit tests in Apex.

Why?

Well, after a few scrambles to fix broken Apex code in a production org, I realized that there is a lot of wisdom in the "Testing Best Practices" article in the Force.com Apex Code Developer's Guide, Version 23.0.

The trouble with implementing this wisdom consistently and repeatedly is that when you have to setup test records for a bazillion objects, the exercise becomes extremely tedious and painful... not to mention error-prone.

Imagine writing the same boilerplate code for 5 objects, with 3 records per object, with each record having 10 fields filled in. At a minimum, you're looking at writing 5 x 3 x 10 = 150 assignment statements. And then, what do you do about Lookup and Master-Detail relationships?

And you have to write this code all over again for every single test class you write? And how do users trust that you really understand what you're doing with the data?

Added Benefit

Who wants to create test data? Well, if your users really care about the system, then they would want to create the test data! This is a great collaborative exercise where admins, developers and users can all learn from each other by reconciling how the system was intended to be used and how the system is actually used.

Developers can setup the spreadsheets and then get functional users to fill in the test data! This frees up the developer's time to focus on the important part that only (s)he can do: actually writing the unit test methods.

Thursday, October 13, 2011

Best Practice for Automated Task Creation?

In retrospect, I think a best practice for automating task creation in Salesforce is to use workflow rules instead of Apex triggers. The primary reason is that the tasks are easier to manage and can be avoided during a mass update is the user leaves the "trigger workflow" checkbox unmarked in the import wizard.

Tuesday, August 16, 2011

Reliably Getting Record Type ID's for Test Classes

I'm going to share this simple way of reliably getting a record type ID in Salesforce for testing purposes. Using this method will guarantee that the right record type is retrieved for the right object.

All one has to do in the test class is... get ready for the ridiculously long line of code...

private static final Id RECORD_TYPE_NAME_RECORD_TYPE_ID =
Schema.SObjectType.sObjectApiName.getRecordTypeInfosByName().get('Record Type Name').getRecordTypeId();


Thank you, Salesforce, for sObject Describe Results.

Thursday, August 11, 2011

Trigger Context to Provide Related Record Information

Developers who have worked with triggers may already know that related object information is not available in the context of a trigger. To elaborate on the problem, I'll give an example.

Take the Contact object. If I write a trigger on the Contact object, I'll have access to the AccountId value for each Contact handled by the trigger, but I will not have access to the related values such as Account.Name or Account.Owner.Name. Usually, to get around this, I would need to write SOQL within the trigger to retrieve the related values for use within the trigger.

This has personally been a great pain in my side, and it only seems to grow more intense as time goes by. Now, I'm stuck at trying to figure out how to get at those related values in another class that's used by the trigger. After some frustration, I came up with an idea.

What if I used a static variable stored in a utility class that I could set and get at will, wherever as long as I'm within the same trigger context? Well, that'd be pretty cool. To that end, I arrived at the concept for a TriggerContext class.

I won't spend too much time discussing it, as a demo's worth a million words. The classes and triggers inside TriggerContextDemo-1.0.zip can be added to any sandbox or developer org. To see the effects, simply create a new contact record, save it, and then update it by associating an account.

As I'm wrapping up this post, I realize that another, more straightforward approach to handling this could be to have the trigger's associated utility class automatically assign the related records back to the trigger records by writing to the sObject fields and not to the record ID fields. I'll look into this more tomorrow as I try to handle related values in a real project.

Friday, July 29, 2011

Efficient Regex Pattern for Getting Hashtags

After digging around the Internet for a while and not finding a regex pattern that was able to produce all of the hashtags in a String, I finally created my own based on information I gathered from a few other places.

\B#[a-zA-Z][a-zA-Z0-9]+

My sources include the following:

I took this information and created a method in Salesforce to grab all of the hashtags from a String and return it in a Set, as shown below.

/**
 * Get the Set of hashtags (including
 * the '#' character) used within a String in
 * all lower case, for ease of comparison.
 *
 * @param  text The String text to analyze.
 * @return      The Set of hashtags
 *              used within the text.
 */
public static Set getHashtagSet(
        String text) {
    
    // Instantiate the resulting set.
    
    Set hashtagSet = new Set();
    
    // Only look for hashtags if text is given.
    
    if (text != null) {
        Pattern hashtagPattern = Pattern.compile(
                '\\B#[a-zA-Z][a-zA-Z0-9]+');
        Matcher hashtagMatcher =
                hashtagPattern.matcher(text);
        
        while (hashtagMatcher.find()) {
            hashtagSet.add(
                hashtagMatcher.group().toLowerCase());
        }   // while (hashtagMatcher.find())
    }   // if (text != null)
    
    // Return the results.
    
    System.debug('hashtagSet = ' + hashtagSet);
    
    return hashtagSet;
}   // public Set getHashtagSet(String)

Sunday, March 20, 2011

ISBLANK(String) Returning false When String.length() Returns 0

I discovered this with version 21.0 of the Salesforce API: When a String's length is 0, the ISBLANK(String) Visualforce function will actually return false instead of true.

Visualforce Developer's Guide, Version 21.0 describes the ISBLANK function as follows:

Determines if an expression has a value and returns TRUE if it does not. If it contains a value, this function returns FALSE.

A search for ISNULL in the documentation returned no real results, which makes me wonder if ISNULL has been unofficially deprecated in favor of ISBLANK.

So, it appears that the only way to know for sure whether a String input has been blanked out is to use the length method and compare it to 0.

Friday, February 25, 2011

Conversion Error setting value 'value1 value2 value3' for '#{myMultiselectPicklistValue}'.

Salesforce is great. Apex is great. Visualforce is great.

Until you run into bizarre errors with no obvious explanation, such as the following error:
Conversion Error setting value 'value1 value2 value3' for '#{myMultiselectPicklistValue}'.

This error comes from trying to save the selections from an apex:selectCheckboxes component into a multi-select picklist field. You would think that it's as easy as simply specifying {!property} for the value attribute of the apex:selectCheckboxes component. But, no, it's not.

Multi-select picklist values are stored as String values, with each option delimited with a semicolon.  This is great to know, but what happens with apex:selectCheckboxes? apex:selectCheckboxes expects a List<String>! This discussion board post hints at the annoyance awaiting developers: "Checkboxes not saving".

Basically, to spell it out for myself and for others, here's what we have to do as developers working around this problem.

What we want to write is:

<apex:selectcheckboxes value="{!mPicklistValue}">
... and in the controller ...

public String mPicklistValue { get; set; }

Instead, what we have to write is:

<apex:selectcheckboxes value="{!mPicklistValues}">
... and in the overblown controller ...
private String mPicklistValue;
public List<String> getMPicklistValues() {
    List<String> values = null;
    // Convert mPicklistValue into List of 
    // String values

    if (mPicklistValue != null)
        values = mPicklistValue.split(';');

    return values;
}   // List<String> getMPicklistValues()
public void setMPicklistValues(
        List<String> values) {

    // Convert values into a semilcolon-delimited
    // String value

    if (values == null) {
        mPicklistValue = null;
    }
    else {
        mPicklistValue = '';
        
        for (String value : values) {
            mPicklistValue += value + ';';
        }
    }
}   // void setMPicklistValues(List<String>)

Note the plural name of the custom getter and setter methods. Cheers, indeed.