apex-lang 1.17 released

I’m happy to announce I’ve released version 1.17 of apex-lang! It’s been a while since I’ve published a release so this one has a substantial amount of new features.

Links for apex-lang version 1.17

Changes since 1.15

Once you install version 1.17, any of the following code snippets can be run in your org. Note: the snippets assume you installed the managed package; if you instead installed the code directly to your org, remove the “al.” prefix before running.

DatabaseUtils

New class that allows records to be easily retrieved by object id(s) alone; it even allows you to do “select *” if you so choose.  The query method has magic behind it to determine object name of the corresponding id(s).

//insert an account
final Account x = new Account(Name='Test 123', NumberOfEmployees=1);
insert x;

//re-load the account by id only, following call is analogous to select *
final Account y = (Account) al.DatabaseUtils.query(x.id);
System.assertEquals(x.id,y.id);
System.assertEquals(x.name,y.name);
System.assertEquals(x.NumberOfEmployees,y.NumberOfEmployees);

//of course, the more responsible thing to do is to specify the fields you want
final Account z = (Account) al.DatabaseUtils.query(x.id,new Set{'name'});
System.assertEquals(x.id,z.id);
System.assertEquals(x.name,z.name);
//uncomment the following row and you'll get an exception because NumberOfEmployees wasn't retrieved
//System.assertEquals(x.NumberOfEmployees,z.NumberOfEmployees);

//the magic behind this code is the retrieveObjectName() method
//it's probably useful in other context as well
final String objectName = al.DatabaseUtils.retrieveObjectName(''+x.id);
System.assertEquals('account',StringUtils.lowerCase(objectName));

HttpUtils

A new class which enables 100% coverage of web service callouts.  Per this note, the Http.send() method cannot be executed in unit tests.  It’s really a shame that salesforce hasn’t created a mechanism for getting around this similar to Test.setFixedSearchResults() for SOSL queries.  I thought they would’ve by now but alas, no progress.  I’ve seen lots of good ways for getting around this; however, most involve muddying up your real code with test related code (by real code, I mean code that isn’t a unit test); in other words, there’s an if(Test.isRunningTest()) check somewhere in your real code.  I’d much rather keep all my unit testing logic in my unit tests themselves and that’s precisely what HttpUtils allows you to do.  See following example for an explanation.


//save this class and execute unit tests
public class MyService{
    private static final String TEST_RESPONSE = 'a response string - might be xml, json, etc...';
    private HttpRequest request;

    public MyService(){
        request = new HttpRequest();
        request.setEndpoint('http://somewhere.com');
        request.setMethod('GET');
    }

    public String theWayYoureProbablyInvokingCallout(){
        String response = null;
        //following line is such a hack; this is test code in your real code!
        if(Test.isRunningTest()) response = TEST_RESPONSE;
        //but you have to do it because the following line can't be covered #insanity
        else response = (new Http()).send(request).getBody();
        return response;
    }

    public String sameResultButEasierToTest(){
        return al.HttpUtils.sendReturnBody(request);
    }

    private static testmethod void test_theWayYoureProbablyInvokingCallout(){
        System.assertEquals(TEST_RESPONSE,new MyService().theWayYoureProbablyInvokingCallout());
    }

    private static testmethod void test_sameResultButEasierToTest(){
        //Note the call to push test here.  Your pushing your expected result immediately before you check for it.
        al.HttpUtils.pushTest(TEST_RESPONSE);
        System.assertEquals(TEST_RESPONSE,new MyService().sameResultButEasierToTest());
    }

}

Here are the send methods you can invoke on HttpUtils:

  • global static HttpResponse send(HttpRequest request)
  • global static String sendReturnBody(HttpRequest request)
  • global static Dom.Document sendReturnDocument(HttpRequest request)
  • global static XmlStreamReader sendReturnReader(HttpRequest request)

SObjectSortByFieldComparator

Another new class that simplifies doing in-memory sort of SObjects (vs using ArrayUtils.qsort).  You simply give the class a list of SObjects and tell it the field to sort against.


//sort a list of accounts on field NumberOfEmployees
List unsorted = new List{
     new Account(name='A',NumberOfEmployees=2)
    ,new Account(name='D',NumberOfEmployees=3)
    ,new Account(name='C',NumberOfEmployees=0)
    ,new Account(name='B',NumberOfEmployees=1)
};
List sorted = al.SObjectSortByFieldComparator.qsort(unsorted,'NumberOfEmployees');
System.assertNotEquals(null,sorted);
System.assertEquals(4,sorted.size());
System.assertEquals(0,sorted.get(0).get('NumberOfEmployees'));
System.assertEquals(1,sorted.get(1).get('NumberOfEmployees'));
System.assertEquals(2,sorted.get(2).get('NumberOfEmployees'));
System.assertEquals(3,sorted.get(3).get('NumberOfEmployees'));

//you could even mix SObjects so long as they share a field
unsorted = new List{
     new Account(name='A')
    ,new Opportunity(name='D')
    ,new Document(name='C')
    ,new Campaign(name='B')
};
//sort is on name field by default; false means sort descending
sorted = al.SObjectSortByFieldComparator.qsort(unsorted, false);
System.assertNotEquals(null,sorted);
System.assertEquals(4,sorted.size());
System.assertEquals('D',sorted.get(0).get('name'));
System.assertEquals('C',sorted.get(1).get('name'));
System.assertEquals('B',sorted.get(2).get('name'));
System.assertEquals('A',sorted.get(3).get('name'));

MapUtils

Added methods for doing trim, lowerCase, upperCase en masse to keys or values on a map.

al.MapUtils.assertEquals(
  new Map{'a'=>'Y','b'=>'Z'}
  ,al.MapUtils.lowerCaseKeys(new Map{'A'=>'Y','B'=>'Z'})
);

al.MapUtils.assertEquals(
  new Map{'a'=>'Y','b'=>'Z'}
  ,al.MapUtils.upperCaseValues(new Map{'a'=>'y','b'=>'z'})
);

SetUtils

Added methods to make switching between lists and sets easier; trim, lowerCase, upperCase en masse methods; new method pluckString.

//switching between String sets and lists
final Set myStrSet1 = new Set{'a','b'};
final Set myStrSet2 = al.SetUtils.listToSet(al.SetUtils.setToList(myStrSet1));
System.assertEquals(myStrSet1,myStrSet2);

//switching between SObject sets and lists
final Set mySObjSet1 = new Set{new Account(name='a'),new Account(name='b')};
final Set mySObjSet2 = al.SetUtils.listToSet(al.SetUtils.setToList(mySObjSet1));
System.assertEquals(mySObjSet1,mySObjSet2);

//plucking an attribute from a set of SObjects
System.assertEquals(myStrSet1,al.SetUtils.pluckString(mySObjSet1,'name'));

//trim and lower case all values in a set
System.assertEquals(myStrSet1,al.SetUtils.lowerCase(al.SetUtils.trim(new Set{'  A\n\t  ','  B '})));

StringUtils

Added a new stripMarkup() method which will remove all HTML/XML markup from a string.

System.assertEquals(null, al.StringUtils.stripMarkup(null));
System.assertEquals('', al.StringUtils.stripMarkup(''));
System.assertEquals('Title! ABC 123', al.StringUtils.stripMarkup('  Title!   ABC 123  '));
System.assertEquals('Title! ABC 123', al.StringUtils.stripMarkup('<html><body><h1>Title!</h1><p>ABC 123</p></html>'));

Note about test coverage

I think one of the best decisions salesforce made when designing Apex was to require 75% code coverage. Salesforce doesn’t get enough good publicity for this because that decision has forced many folks – myself certainly included – to become better developers. With the unit tests for apex-lang, I decided to buy into the Test Driven Development philosophy even further. My standard has been (1) every line must be tested (100% code coverage) and (2) the unit tests actually verify functionality. That latter part is key because code coverage in and of itself really doesn’t buy you anything – it’s the verification that adds value.

That being said, I wanted to note an exception to this rule that I’ve introduced in 1.17 and that’s the HttpUtils class. Like I said above, the Http.send() method cannot be called in a unit test; however, in order for the HttpUtils class to be of value, it obviously needs to call it. So, in the interest of making unit testing eaiser, I’ve sacrificed some of apex-lang’s code coverage by a small number: I believe HttpUtils is 98% covered. But it’s not 100% so that’s why I mentioned it.

5 Comments

  1. 1
    Ray says:

    “Salesforce doesn’t get enough good publicity for this because that decision has forced many folks – myself certainly included – to become better developers.”

    I agree. From what I’ve seen, those who tend to complain are most resistant to becoming better developers.

  2. 2
    Abhinav Gupta says:

    Good going Richard .. !

  3. 3
    MJ WIvell says:

    apex-lang rocks! Richard mentioned he’s “Happy to announce . . .”

    I call using apex-lang happy fun time.

    It makes life so much easier! Keep up the good work!

  4. 4
    Alex S. says:

    Thanks for the new release Richard!

    Do you plan to post this library up on GitHub anytime soon?

  5. 5
    Richard says:

    You’re the 2nd person to ask me that in last few days. Seems like I need to.

Leave a comment