Friday, 8 August 2025

Software Testing on the Salesforce Platform


Introduction

Since the start of my developer journey with Salesforce, there's one phrase I've trotted out with remarkable regularity:

"When I have more time I'm going to write a book about testing". 

Typically this would be when I was reviewing unit tests and found the developers unable to tell me why they'd created a particular test, other than it caused a few lines of code to be executed and thus covered for deployment to production. Other times it was after talking to graduates to find out that UK universities still don't cover unit testing in Computer Science degrees, and they didn't really know how to get started. I'm now at the point where I have more time, so after talking the talk, it's time to start walking the walk. 

TL;DR - it's a work in progress, but available for purchase on Leanpub at Software Testing on the Salesforce Platform

How to Publish?

I didn't want to go the traditional route, but also didn't want the vanilla self-publishing experience. Instead I was looking for some kind of iterative mechanism where I didn't spend a year creating a single point in time snapshot and then never returned to it (outside of spending another year on another point in time second edition etc). I also didn't want something that cost $50+. While that's not a huge amount of money in places like Western Europe and the USA, there are other locations where it's prohibitive. 

I remembered reading that Steven Sinofsky wrote his Hardcore Software book on Substack, but when I dug into this it was a $100/year subscription over 2 years, so considerably more expensive rather than less. Also, while this was a reasonable way to break up the writing, there's no easy way to combine all the newsletters back into a book, hence he published it as a book too. 

Enter Leanpub

I was familiar with Leanpub having bought a couple of books when I was learning Node.JS, and when I looked into publishing on that platform it was pretty much a perfect fit for what I was trying to do:

  • Publish Early, Publish Often.

This is a core principle of Leanpub  - rather than waiting until everything is complete, you can publish as soon as you feel you have enough content to make it worthwhile, then you push out updates as and when you feel it is appropriate. This also allows you to pivot based on reader feedback. It also allows you to give up if there is zero interest I guess :)

  •  Readers Pay Once

    Rather than paying out for the initial edition, then paying again for each future edition, once you buy a book on Leanpub you get all the updates included forever. 

  • Flexible Pricing

    I can set (and change) the price as I desire. I also have the option to set a minimum price (what I will sell it for) and a suggested price (what I'd like to sell it for). That said, there's nothing to stop someone paying more than these prices if they really want to spend $50 or more!

Version 1

The first version of Software Testing on the Salesforce Platform weighs in at 147 pages and has a suggested/minimum price of $5.99 - it is by no means the finished article, as I still have a lot of ground I want to cover. There is potential downside to purchasing at this time, in that I'm under no obligation to complete it so this might be all you get. I do intend to finish it, and I feel that it represents value for money in its current form, but your opinion may differ. 

To reiterate on the pricing, if you buy this version then you get every subsequent version free forever. The price will increase as I add content, so early adopters are rewarded for believing in me with a chance to get in at the rock bottom.

When you purchase on Leanpub an account is automatically created for you - this isn't to allow me to pester you, but to allow you to access the latest version when it is published. 



Tuesday, 5 August 2025

Agentforce Custom Lightning Types

Introduction

The Summer '25 release of Salesforce (or maybe prior, given Agentforce releases are on a monthly cycle) introduced Custom Lightning Types, which finally gives us a degree of fine-grained control over the user interface. At the time of writing (August 2025) this is only for actions that use Apex classes for requests and responses.

In essence, a Custom Lightning Type configures an Agentforce user interface override for an Apex class. The Apex class can either be used as the input to send information to an Agentforce action, or as the output containing the response to the user request. 

Example

I've been playing around with a Custom Lighting Type to display overview information from my Limit Tracker that I created for my session at London's Calling 2025. For this I wanted two custom types:

  • An input that captured the number of days of Limit Snapshots to include in the overview
  • An output that showed the Heap and CPU details from the snapshots
I also wanted these to be more interesting than a simple form field to capture an integer and a list of values, but that only comes into play when developing the Lightning Web Components to handle the user interface, so I'll come to those later.

Output

My custom Apex action returns the following class:

public class LimitConsumption { 
    @InvocableVariable
    public String name;
                
    @InvocableVariable
    public List<LimitSnapshot> snapshots;         
        
    public LimitConsumption(String name, List<LimitSnapshot> snapshots) {
        this.name = name;
        this.snapshots = snapshots;
    }
}

It can sometimes be tricky to pinpoint the exact class returned from an action, as they tend to be inner classes and nested inside response wrappers. An easy way to find out exactly which class Agentforce will use is to go through the New Action process and see what it picks up for the output:


If you aren't from a developer background, the class reference might look a bit odd, but it breaks down as:

  • c__ this is the namespace the class is present in. c__ means the default (or lack of) namespace. 
  • LimitConsumptionOverview is the outer, or containing class
  • The $ separator indicates this is an inner class of LimitConsumptionOverview
  • LimitConsumption is the name of the actual inner class 
Once I know which class my type with will be working with, I can create an entry in the lightningTypes source folder, in this case limitConsumptionResponse, to make it clear this is used to render the response from an Agent.
   
The schema.json file configures the type to work with my custom class:
{
  "title": "Limit Consumption",
  "description": "Overview of limit consumption by a particular piece of Apex code",
  "lightning:type": "@apexClassType/c__LimitConsumptionOverview$LimitConsumption"
}

The lightning:type entry is the key piece of wiring that couples this Custom Lightning Type with my Apex class.

Next I had to decide how I wanted to render this information. Something that couldn't easily be found on the regular detail pages was appealing, and as it is handled by a Lightning Web Component I have access to the full power of JavaScript and the Lightning Design System. I decided to return a tabbed interface containing a chart :



and a list of values:



Note that I've taken a leaf out of the Salesforce examples and my custom Apex action returns the same list of fake data regardless of how it's invoked.

You can find the Lightning Web Component at : limitConsumption - it uses the Chart.js library to create the bar chart.

Once my Lightning Web Component is in place, I need to create a file in my Custom Lightning Type folder named lightningDesktopGenAi named renderer.json:

  {
    "collection": {
      "renderer": {
        "componentOverrides": {
          "$": {
            "definition": "c/limitConsumption"
          }
        }
      }
    }
  }

The key aspect here is the componentOverrides which defines my Lightning Web Component (limitConsumption in the default namespace) as the override to render the type. 

Input

My custom Apex action relies on the following input class:

public with sharing class SliderIntegerWrapper {
    @InvocableVariable
    public Integer value;
}

Note that this is simply wrapping an Integer value - while lots of the examples I've seen are around complex classes containing multiple values/nested classes, they don't have to. In this case I just want a funkier way to enter the value, so I need an Apex class to hold it.

To capture the information I decided to go with a slider component - note that this did give Agentforce a little trouble with the rendering - once the value got to double figures it wrapped to the next line!


Similar to the output, I create a lightningType folder named sliderInteger and define the schema.json to couple the type to my class:

{
  "title": "Slider Integer",
  "description": "Slider Integer",
  "lightning:type": "@apexClassType/c__SliderIntegerWrapper"
}

as this time my class is top level, the entry just contains c__ (for the default namespace) and the name of the class.

The Lightning Web Component that provides the input slider can be found at sliderInteger

This time, once my Lightning Web Component is in place, I need to define a lightningDesktopGenAi/editor.json file to define it as the override:

{
  "editor": {
    "componentOverrides": {
      "$": {
        "definition": "c/sliderInteger"
      }
    }
  }
}

The Agent Action

Now that I have my Custom Lightning Types defined and the overrides configured, I can create the Agentforce action that makes use of them, based on my LimitConsumptionOverview invocable Apex class. When defining the inputs I choose my SliderInteger type for the daysWrapper :


and for outputting the limit consumption information, I choose the LimitConsumptionResponse type:


Executing this action brings both my types into play, as can be seen from the following short video:




Lessons Learned

Something I'd strongly recommend when working with Custom Lightning Types is to build yourself a simple test page so that you can try them out without having to go via Agentforce every time you make a change. While it doesn't sound onerous, making requests and supplying inputs gets tedious very quickly! I created a test harness Lightning Web Component (limitConsumptionHarness) that sends fake data to the output component and handles the event from the input component, and it's saved me a lot of time.

The first scratch org I tried this in, I'd already used the example from the Salesforce docs, and for some reason only that one would be picked up. After spending a couple of hours trying various things, and creating yet another set of custom types, I spun up a new scratch org and it all worked fine. No idea what went wrong, but by the same token I was learning and tweaking so it could have been anything.

Make sure to use distinct names for your types, classes and Lightning Web Components. My first attempt I used the same name for the type and the component and quickly lost track of what I was configuring where.

More Information