Identifying Apex tests to run using wildcards in sf:deploy

When developing code including tests that builds on a managed package it is typically necessary to exclude the managed package tests from the test runs. For example, the new code might add an additional data constraint that causes a managed package test to fail because its data setup violates the constraint. (There is also the secondary issue that the managed package tests may slow the development cycle if they take many minutes to run.)

The sf:deploy Ant task supports a flag to run all tests or alternatively accepts the names of the test classes to run. If the number of tests involved is small, the latter option works fine. But if you have a large number of tests it starts to become tedious and error prone to maintain the list of tests. This is the same problem that Java projects using the JUnit Ant task face and there the solution is to allow the set of tests that are run to be driven by file name matches in the source tree via the batchtest nested element.

I’ve added this mechanism to the DeployWithXmlReportTask (an extension of sf:deploy) that is available in the force-deploy-with-xml-report-task Google code project. Below is an example of how to use it in a project where all test classes follow the convention of having their file names end in “Test”.

<path id="ant.additions.classpath">
    <fileset dir="ant"/>
</path>
<target name="deployAndTestAndReport">
    <taskdef
        name="sfdeploy"
        classname="com.claimvantage.force.ant.DeployWithXmlReportTask"
        classpathref="ant.additions.classpath"
        />
    <delete dir="test-report-xml" quiet="true"/>
    <echo message="deploying to ${sf.username}"/>
    <sfdeploy
            username="${sf.username}"
            password="${sf.password}"
            serverurl="${sf.serverurl}"
            deployRoot="src"
            runalltests="false"
            maxpoll="60"
            junitreportdir="test-report-xml"
            >
        <!-- Run all tests that match the include file name pattern (so avoiding running managed package tests) -->
        <batchtest>
            <fileset dir="src/classes">
                <include name="*Test.cls"/>
            </fileset>
        </batchtest>
    </sfdeploy>
</target>

“Don’t rebase dates” option in “Export to DOT file”

When exporting a copy of your Trialforce master org that contains a configured version of your managed released package, there is a “Don’t rebase dates” option. Salesforce support provided this explanation:

  • If you check the “Don’t Rebase Dates” checkbox, it means don’t rebase the dates. In that case, you preserve the record date in the DOT’ted org. For E.g.:, if the record date in source org is 11/24/10, the record date in DOT’ted org is 11/24/10.
  • If you uncheck the “Don’t Rebase Dates” checkbox, it means rebase the dates. In that case, the record date in the DOT’ted org = signup date of the DOT’ted org + (created date of the record in source org – signup date of the source org).

Stack traces in the debug log

About the only time that you get a stack trace – the key line number information to find where an unexpected exception is coming from – in Force.com is for errors that are not handled and get emailed to the developer. If you handle an exception there is no way to extract the stack trace and it is lost. (Stack Trace with Exceptions is over 2 years old and unlikely to ever get implemented.) This is bad news during development and much worse news when you are in production.

While trying to track down a NullPointerException I resorted to turning on the (usually overly verbose) logging and at first glance didn’t think it was going to be much help. But then I noticed that in fact the line numbers are there in square brackets meaning that this debug log entry:

13:30:17.139|EXCEPTION_THROWN|[157]|System.NullPointerException: Attempt to de-reference a null object
13:30:17.139|METHOD_EXIT|[132]|IntakeBusinessLogic.benefitIndependent()
13:30:17.139|METHOD_EXIT|[176]|IntakeBusinessLogic.execute()
13:30:17.139|METHOD_EXIT|[143]|CmsCalloutController.doSave()
13:30:17.139|CODE_UNIT_FINISHED|CmsCalloutController invoke(save)

contains the same line number information that this stack trace does just organized a little differently:

caused by: System.NullPointerException: Attempt to de-reference a null object

Class.IntakeBusinessLogic.benefitIndependent: line 157, column 13
Class.IntakeBusinessLogic.execute: line 132, column 9
Class.CmsCalloutController.doSave: line 176, column 74
Class.CmsCalloutController.save: line 143, column 32
External entry point

So at least in development you can (eventually) get hold of the line number information you need to fix the problem.

Hyperlink formula fields

I have a data model that has multiple levels of master-detail relationships. While a hyperlink to the immediate parent object is easily available, adding a link to a more distant ancestor, the Parent__c link on the GrandChild__c object in this diagram, is more awkward:

If you make Parent__c a “Lookup Relationship” field then you must also add logic such as a trigger to populate the Id value correctly whatever changes are made. This can be awkward to code and requires quite a lot of test cases.

If you make Parent__c a “Formula” field, then at first sight it is not possible to have a hyperlink presented in the user interface because the “Formula Return Type” options do not include such an option.

But a colleague has just passed a solution on to me that he found in part in the post Lookup Formula Fields. Using a “Formula Return Type” of “Text” this formula can be used:

HYPERLINK(Child__r.Parent__r.Id , Child__r.Parent__r.Name, "_self")

resulting in a hyperlink to the Parent object that opens within the existing sidebar and header when clicked and most importantly requires no additional logic to keep the values updated.

Constraining a detail field depending on a master field in a trigger

I’ve implemented this pattern several times with slight variations and each time had to do some head scratching to get it right. So here is the pattern expressed in somewhat abstract terms. The aim is to disallow a change to a Detail object field if its Master object has a field of a particular value. In my case these have been status fields.

Here is the trigger:

trigger DetailBeforeTrigger on Detail__c (before insert, before update) {
    // Create a map of the master object id to the detail objects
    Map<Id, List<Detail__c>> masterToDetails = new Map<Id, List<Detail__c>>();
    for (Detail__c newDetail : Trigger.new) {
        Detail__c oldDetail = Trigger.oldMap != null ? Trigger.oldMap.get(newDetail.Id) : null;
        String oldStatus = oldDetail != null ? oldDetail.Status__c : null;
        String newStatus = newDetail.Status__c;
        // Only add where the field has changed and to the specific value
        if (oldStatus != newStatus && newStatus == 'Approved') {
            List<Detail__c> ds = masterToDetails.get(newDetail.Master__c);
            if (ds == null) {
                ds = new List<Detail__c>();
                masterToDetails.put(newDetail.Master__c, ds);
            }
            ds.add(newDetail);
        }
    }
    // Add an error to the detail objects where the master field has a particular value
    if (masterToDetails.keySet().size() > 0) {
        for (Master__c m : [select Id, Status__c from Master__c where Id in :masterToDetails.keySet()]) {
            if (m.Status__c == 'Closed') {
                for (Detail__c d : masterToDetails.get(m.Id)) {
                    d.Status__c.addError('Detail Status cannot be "Approved" when the Master Status is "Closed"');
                }
            }
        }
    }
}

This code illustrates:

  • Checking if a field value has changed and only doing work if it has
  • Holding the 1:Many information in a Map
  • Ensuring only one SOQL query is done whether there is one Detail object or many
  • Reporting the error on a field so that it is clearly presented in the default UI