Showing posts with label best practice. Show all posts
Showing posts with label best practice. 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

Monday, January 16, 2012

What is an enrollment product in higher education?

When it comes to enrollment management, I hypothesize that institutions in the education industry share many common administrative concerns as companies in other industries.
  1. What's our forecast for the next quarter or year?
  2. How much are we allocating to Marketing?
  3. What's our historical ROI on past campaigns? And what are we expecting for new Marketing campaigns this year?

But one big difference I see between education institutions and other companies is how the above questions are answered. While other companies usually answer Questions 1 and 3 above with dollar amounts, education institutions (from the enrollment perspective) answer with student headcount or with class enrollments.

This may seem like a trivial distinction, but how useful would it be for Apple to announce their forecasts using the numbers of iPhones, iPads and MacBooks it plans to sell in the next year? Or worse yet, what if Apple simply said it would sell 1,000,000 products this year? And how would Apple's Marketing department calculate ROI for its myriad campaigns if this was the case?

Back in the world of education, I think there is a mental roadblock which prevents us from recognizing what our products truly are. Culturally, we, the administrators, think highly of education (which we should). But perhaps we've become ignorant of the underlying business model for education, which in the simplest sense revolves around the sale of products to customers. Put another way, the business model revolves around the enrollment of students in classes.

Let's explore the potential similarities and see whether they are legitimate.
  • Student vs. Customer
  • Enrollment vs. Sale
  • Course vs. Product
  • Class vs. Lot
  • Seat in a class vs. Asset

We have recruiters, enrollment coaches and advisers who work with students to identify which courses to take to best meet their career aspirations and personal goals. Once the courses are identified, our staff help students to register for classes, thereby reserving their seats in the upcoming term. Once the add/drop period ends for a term, the enrollments are "closed" and invoices are sent to the students.

Other companies have salespeople working with customers to pick the most suitable products for each customer. Once customers decide to buy, they put in orders that are fulfilled in lots, resulting in assets (and associated invoices) delivered to each individual customer.

So, how different is an education institution from other companies? And if it's not that different, could adoption (or at least reconciliation) of real sales terminology be the way for an education institution to start answering the tough questions directly with real dollar amounts?

Note: I speak largely from my own experiences, and I recognize that there are other institutions which are already far along on the path of leveraging the concept of products in their CRM operations.

Friday, January 13, 2012

What is a program in higher education?

As system maintenance becomes more difficult, and as user dissatisfaction with reporting grows, I've been thinking a lot about one of the most controversial 4-word questions in higher education: "What is a program?"

I still remember some of the answers from when I first asked the question on Chatter last October.
  • "I would define a program as a sequence of college level courses (credit bearing) that lead a student to a certificate, degree, or diploma."
  • "I think specializations would be subcategories or tags within programs. I don't see Fast-Track degrees as separate programs. To me, that is similar to online vs. on-campus delivery. Same program, different delivery methods."
  • "I think what makes one program different from another is its required content. FT requires a certain and specific content (in a specific order even), so I would categorize it as a different program. However, online v on-ground wouldn't be a different program. Specializations have specific required content, so even though they don't end in different degrees, I would probably classify them as different programs."

Unfortunately, we never reached a conclusive answer. And the systems architect in me wanted a firm definition of a "program" in higher education so that I could help to design a system that best fit our business processes. Argh! "Frustrated" only begins to describe how I felt at the time.

But now, I have a different take on the question, with much less frustration.

For starters, let me back up and say something about our industry. I think the notion that higher education is somehow inherently different from all of the other industries out there has made me blind to the broader definition of the word "program". People in higher education can elevate and romanticize the industry to the point where colleagues will frown upon the application of business terminology (like "programs") to our operations. By removing the higher education context from the word, I feel like my grasp of "programs" has improved by leaps and bounds.

Let's jump back start with a program in higher education and see if we can discern any important characteristics.

All programs in higher education seem to have the following:
  • Eligibility criteria: Who is eligible to be considered for entry into the program? For example, to be eligible for a master's degree program, an individual should currently hold a bachelor's degree.
  • Entry procedure: If eligible, how does a person enter the program? Usually, a person enters a program by submitting an application for admission and then being accepted after a formal review of that application.
  • Curriculum: Once in the program, what courses is a student expected to take?
  • Services and perks: While in the program, what services or perks can a student expect to receive or have access to?
  • Maintaining program membership: What does a student have to do to remain in a program? For example, if a student does not complete a program within 7 years then that student is effectively out of the program and must apply again for re-entry.
  • Completion criteria: What does a student have to do to complete the program? Usually this involves passing all of the courses in a prescribed curriculum.
  • Reward for program completion: What does a student get for completing a program and graduating? In higher education, a graduate typically receives a combination of degree, major, minor, concentration and specialization from the institution to attest that the graduate now holds certain level of skill and knowledge within a field of expertise.

What's so special about this notion of a program is that this concept is not rigidly constrained by the generic, systematic definition (to which there are currently many exceptions) at our institution that programs are defined by distinct combinations of degree, major, and concentration.

And the real value of this discussion may be simply acknowledging that creating, changing, splitting, merging and retiring programs are more parts of an art than a science, which is how I imagine programs are treated in other industries like retail and fitness.

To give this concept a test drive: How may the different notions of a "program" play out in the following two scenarios?

Scenario 1: Doctoral program with specializations

Historically, we have had a doctoral program with specializations that was only ever tracked as a single program record in our student information system. However, Marketing would typically promote the handful of specializations as if they were separate programs on our website, because the target audiences were different enough and the rewards were different enough (in the eyes of our students).

In our CRM system (Salesforce), should the program records match 1-for-1 those in our student information system?

Scenario 2: Doctoral program with varying requirements

We also have a doctoral program that has slightly different curricula for students entering with a bachelor's degree compared to students entering with a master's degree. Historically and in the near future, the plan would still be to market this program as a single program on our website. But on the back end, the registrar's office created two separate programs in our student information system in order to better track our applicants and students.

Again, in our CRM system, should the program records match 1-for-1 those in our student information system?

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.