Showing posts with label salesforce developer. Show all posts
Showing posts with label salesforce developer. Show all posts

Friday, 8 August 2025

Software Testing on the Salesforce Platform


Update 10/08/2025 : Second version now pushed and the price has increased to $7.99. 

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. 



Saturday, 30 September 2023

Apex Comparator in Winter 24

Image generated by Stable Diffusion from a prompt by Bob Buzzard

Introduction

The Winter 24 release of Salesforce introduces a few new Apex features, including one that I'm very pleased to see - the Comparator interface. Simply put, this new interface allows the List.sort() method to take a parameter that determines the sort order of the elements in the List. 

The Problem

Now this might not sound like a big change, but it simplifies the support of sorting Lists quite a bit. The way we used to have to do it was for the items in the List to implement the Comparable interface. I've written loads of custom Apex classes over the years that implement this, and it's very straightforward - here's an example from a class that retrieves the code coverage values for all Apex classes in an org and displays them in order of coverage with the lowest covered (problem!) classes first :

public Integer compareTo(Object compareTo) 
{
    CoverageRecord that=(CoverageRecord) compareTo;
    	
    return this.getPercentage()-that.getPercentage();
}	
In this case, implementing compareTo isn't any real overhead - I've created a custom class that contains a whole bunch of information about the coverage for an Apex class - total lines, lines covered etc, so an extra method with a couple of lines of code isn't a big deal. It's a little less convenient if I need to sort a class from an App Exchange package - in that case I'll need to create a new class from scratch to wrap the packaged class and implement the method. If we assume that my coverage class is now in a package, it would look something like :
public class CoverageWrapper 
{
    public BBCOVERAGE__CoverageRecord coverage {get; set;}
    
    public Integer compareTo(Object compareTo) 
    {
        BBCOVERAGE__CoverageRecord that=(BBCOVERAGE__CoverageRecord) compareTo;
    	
        return this.coverage.getPercentage()-that.getPercentage();
    }	
}

Slightly less convenient - I now have a whole new class to maintain to be able to sort, and I have to store all the elements of the list in a CoverageWrapper rather than their original CoverageRecord. Again, not a huge amount of overhead but it gets a bit samey if I'm doing a lot of this kind of thing.

Much the same thing applies if I want to sort sObjects - I need to create a wrapper class and turn my list of sObjects into a list of the wrapper class before I can sort it. All those CPU cycles gone forever!

The Solution

This all changes in Winter 24 with the Comparator interface. I still need to create a class that implements an interface - the Comparator in this case :

public with sharing class CoverageComparator implements Comparator<CoverageRecord> 
{
    public Integer compare(CoverageRecord one, CoverageRecord tother) 
    {
    	return one.getPercentage()-tother.getPercentage();
    }	
}
 

but I don't need to wrap the class/sObject that I am processing in this class and create a new list. Instead I call the new sort() method that takes a Comparator parameter:

List<CoverageRecord> coverageRecords;
    ...
CoverageComparator covComp=new CoverageComparator();
coverageRecords.sort(covComp);

CPU Impact


Regular readers of this blog will know that I'm always interested in the impact of changes on CPU time. In enterprise implementations CPU limits are something that I run up against again and again, so if a new feature improves this I want to know!\

I used my usual methodology to test this - execute anonymous with most logging turned off and Apex at the error level, executing the same code three times and taking the average.

For each test I created the records before capturing the CPU time, then sorted the list. First using a comparator on the list of CoverageRecord objects:
List<CoverageRecord> covRecs=TestData.CreateRecords(100);

Integer startCpu=Limits.getCpuTime();
CoverageComparator covComp=new CoverageComparator();
covRecs.sort(covComp);
Integer stopCpu=Limits.getCpuTime();
System.debug(LoggingLevel.ERROR, 
   'CPU for comparator = ' + (stopCpu-startCpu));

And secondly wrapping them with classes that implement Comparable:

List<CoverageRecord> covRecs=TestData.CreateRecords(100);

Integer startCpu=Limits.getCpuTime();
List<CoverageWrapper> wrappers=new List<CoverageWrapper>();
for (CoverageRecord covRec : covRecs)
{
    CoverageWrapper wrapper=new CoverageWrapper();
    wrapper.coverage=covRec;
    wrappers.add(wrapper);
}

wrappers.sort();
Integer stopCpu=Limits.getCpuTime();
System.debug(LoggingLevel.ERROR, 
      'CPU for comparable = ' + (stopCpu-startCpu));

The results were broadly what I was expecting, as sorting a list in place is always going to be quicker than iterating it, wrapping the members, and then sorting, but it's always good to see the numbers:

  • For 100 records, Comparator took 9 milliseconds versus 11 milliseconds for wrapping
  • For 1,000 records, Comparator took 126 milliseconds versus 150 for wrapping
  • For 10,000 records, Comparator took 1844 millseconds versus 2058 for wrapping
So once you get up to decent sized lists, there's a 10% difference, well worth saving. 

Columbo Close


Just one more thing ... 1844 milliseconds is still quite a chunk of the CPU limit. This is because my original implementation of the CoverageRecord calculates the percentage on demand, based on stored total and covered lines. Clearly in a large list this is being called a lot. I then re-ran the 10,000 test with a new implementation - CoverageRecordCachePercent - which stores the percentage after calculating, which knocked 300 milliseconds, or 15%, off the time. I'm sure that converting again to something that calculated the percentage once the total and covered lines were known and set it as a public property would reduce it further. Your regular reminder that even the smallest method can have an impact if it's being called a large number of times!

More Information




Saturday, 18 February 2023

Conditional LWC Directives in Spring '23

 Introduction

The Spring '23 release of Salesforce is now fully live, after a last minute delay in the US. One of the new features was a set of Lightning Web Component directives for conditional rendering. A relatively minor change, but in my view one with a large impact, as it adds clarity. And as we all know, clarity is the gift that keeps on giving to everyone that works on the component after you.

Old Mechanism

The previous conditional directives were if:true and if:false. Nothing wrong with these per se, but once you start nesting things the clarity starts to leak away. Consider a made up example of a search results page where I want to :

  • display a placeholder if a search hasn't been run
  • display a message if the search has been run but there are no results
  • display the results if the search has been run and there are results
  • display the result count on the right hand side if the search is run and there are results
  • display an additional message if there are more than the 100 results displayed on the page

To assist me I have a few properties - searchRun, hasResults, hasMoreResults, resultCount, and my markup is:
<template if:true={searchRun}>
    <template if:true={hasResults}>
        Results go here!
        <template if:true={hasMoreResults}>
            More than 10 results - please refine your search
        </template>
    </template>
    <div style="float:right">{resultCount} results</div>
    <template if:false={hasResults}>
        Search returned no results
    </template>
 </template>
 <template if:false={searchRun}>
     No search run.
 </template>

While it's not terrible, it's also a bit confusing - lots of true/false and I have to examine the property binding to figure out the relationship between them. The eagle-eyed will also spot the bug - my result count sits outside of the conditional for hasResults, so will be displayed in all cases. This is easily done when the if and else conditionals are independent of each other.

New Mechanism

The new conditionals are lwc:if, lwc:elif and lwc:else. Note that these are dependent on each other - you can't have an lwc:else unless it follows an lwc:if, and it is executed if the lwc:if evaluates to false.

Reworking the markup from above gives me:

<template lwc:if={searchRun}>
    <template lwc:if={hasResults}>
        Results go here!
        <template lwc:if={hasMoreResults}>
            More than 10 results - please refine your search
        </template>
        <div style="float:right">{resultCount} results</div>
    </template>
    <template lwc:else>
        Search returned no results
    </template>
</template>
<template lwc:else>
    No search run.
</template>

which I think is much clearer. I can easily see the markup that will render when the properties evaluate to true or not, and I can see the dependencies as the lwc:else (or lwc:elif) always follows an lwc:if (or lwc:elif). Even more so when I collapse the if template body in VS Code:

Another benefit is that I can't misplace my result count div - if I try to place markup between two dependent lwc conditional directives, in this case between lwc:if and lwc:else, it is immediately called out as a problem:


Related Posts

I've waxed lyrical about clarity in the past, regarding the System.Assert class introduced in Winter '23.




Sunday, 20 November 2022

LWC Alerts in Winter 23


Introduction

The Winter 23 release of Salesforce provided something that, in my view, we've been desperately seeking since the original (Aura) Lightning Components broke cover in 2014 - modal alerts provided by the platform. I'd imagine there are hundreds if not thousands of modal implementations out there, mostly based on the Lightning Design System styling, and all being maintained separately. Some in Aura, some in LWC, but all duplicating effort.

I feel like we have cross-origin alert blocking in Chrome to thank for this - if that wasn't breaking things then I can't see Salesforce would suddenly have prioritised it after all these years - but it doesn't matter how we got them, we have them!

Show Me The Code!

The alerts are refreshingly simple to use too - simply import LightningAlert:

import LightningAlert from 'lightning/alert';

and then execute the LightningAlert.open() function:

    async showAlert() {
        await LightningAlert.open({
            message: 'Here is the alert that will be shown to the user',
            theme: 'warning',
            label: 'Alerted',
            variant: 'header'
        });
    }

and the user sees the alert


The LightningAlert.open() function returns a promise that is resolved when the alert is closed. Note that I've used an async function and the await keyword - I don't have any further processing to carry out while the alert is open, so I use await to stop my function until the user closes the alert. 

Demo Site


When there's a component like this with a number of themes and variants, I typically like to create myself a demo page so I can easily try them all out when I need to. In this case I have a simple form that allows the user to choose the theme and variant, then displays the alert with the selected configuration. 



In the past I'd have exposed this through one of my Free Force sites, but those all disappeared a few months ago so I needed to start again. The new location is https://demo.bobbuzzard.org, which is a Google Site with a custom domain. This particular demo can be found at: https://demo.bobbuzzard.org/lwc/alerts  - it's a Lightning Web Component inside a Visualforce Page using Lightning Out, so with the various layers involved it may take a couple of seconds to render the first time. It does allow guest access though, so worth the trade off in my view. 

Related Posts


Saturday, 24 October 2020

Working with the Salesforce CLI Scanner

Introduction

A couple of weeks ago I had my first exposure to the Salesforce CLI Scanner and it's fair to say I quite liked it - two blog posts in two days is above my usual cadence. The first thing I wanted to do was find out if I could tune it to my specific requirements, which  I was pleased to find out I could by creating custom XPath rules and installing them into my CLI instance.

The next job was to introduce them into my workflow and take action when issues were flagged up, which I've been working on in what time I could carve out.

Customising the Default Rulesets

Some of the default rules from the CLI Scanner weren't applicable to all of my projects - an example being the CRUD/FLS enforcement. If I'm not targeting the app exchange, a lot of the time my code needs to be able to carry out actions on specific objects and fields regardless of the user's profile - I'm relying on running in system mode. I can't see the point in letting this be flagged up as warnings and then hiding or ignoring them, so I wanted to exclude this specific rule. At the time of writing (October 2020) the CLI Scanner doesn't allow rules to be excluded or included at run time, so I needed a way to remove them from the ruleset.

After a small amount of digging around, I found the default ruleset configurations in my ~/.sfdx-scanner directory, so I copied them and removed the CRUD rules. Executing the scanner on my sample codebase showed this had no effect, so I checked the file to make sure I'd removed all instances. I found a couple, removed them and executed the scanner again with the same result. Checking the files again showed the CRUD rules were still in place, and this time I knew that I'd definitely removed all references, so I realised that these configuration files were generated at run time so any changes I made were overwritten. Back to the drawing board.

Cloning the Default Rulesets

As PMD is open source, the rulesets are available in the Github repo, so creating a copy was fairly straightforward. I edited the rulesets to remove the rules that I wasn't interested in, and to change the name - I didn't want to replace the defaults as there are times when I want all the rules applied. 

Something else I learned since my original post is that if you are relying on existing Java rule classes, you don't need to build a JAR file containing the ruleset XML, you can just install the XML file itself, so that saved me a little time. Once I had the custom rulesets in place I was good to go.

Adding to the Workflow

The CLI scanner is now part of my overnight Continuous Integration processing on our newly launched BrightFORUM event management accelerator.  This doesn't have a vast amount of custom code and we've had a very clear set of standards and rules that are applied at code review time, so I wasn't expecting many issues, and I was pleased to find that was the case. All the violations were level 3 or lower as well, and I'd have been pretty annoyed with myself if there were any higher than that. 

Satisfying the Scanner

Clearing the violations was an interesting exercise.  Some were very simple, like unused local variables. Most of the time this was overzealous retaining a reference that wasn't used later, but sometimes it was genuine leftovers from a previous approach. As these had been through code review, I can't see that we'd have picked them up manually in the future unless we'd made further changes.

Others required more effort, like cyclomatic or cognitive complexity, which typically means methods that are trying to do too much at once. An automated approach helps here as it's not down to individual developers deciding that a method is a bit long or a bit complicated, and convince others that is the case. A tool flagging this up takes all the emotion out of it. Fixing these required some refactoring, which I had no problem undertaking as we have excellent test coverage to confirm I didn't break anything inadvertently.

Suppressing Violations

I didn't change the code in every case though - for example, I had a potential SOQL injection flagged up, as the fields in the query were dynamically generated. The string being used in the query had been generated from the built-in schema classes, a couple of lines earlier, so it was guaranteed to contain only valid field names and there was no chance of unescaped user input or the like being included. I could bow to the scanner and escape the string anyway, but that would suggest to anyone reading the code that it was coming from an untrusted source, which wasn't the case. This gave my a chance to try out an Apex annotation that two weeks ago I had no idea existed - @SuppressWarnings.

From the Apex docs : 

This annotation does nothing in Apex but can be used to provide information to third party tools.

To turn off the violation, I added the following annotation to the method that contained the SOQL query:

    @SuppressWarnings('PMD.ApexSOQLInjection')

and the violation is gone. You can suppress multiple warnings by comma separating them inside the single quotes, and you can also apply this at the class level to suppress all warnings of the specified type(s).

This is something that should definitely be used with care - once I've suppressed SOQL injection warnings for a method, any code added that introduces a genuine SOQL injection vulnerability won't get picked up. I'm typically adding a comment to explain why I've suppressed the warning and to say that the method shouldn't be changed without taking this into consideration. I'm also putting regular time into my calendar to revisit the suppress warnings annotations and confirm that I don't have a better approach.

I wouldn't use this annotation to exclude a class from all rules - that could be done, but it would be a big list of suppressions and it would be simpler to exclude the class from those processed. I'd also add a ticket that there is technical debt to be addressed in that class, so that it doesn't get forgotten about and embarrass me in the future.

Takeaways

The key takeaway from my last couple of weeks experience is that automated code scanning leads to cleaner code - things that aren't needed are gone, things that were hard to understand are now easier, and I have some custom rules that ensure that concerns are appropriately separated. Including it in my CI ensures that I pick up any issues as soon as they are added to the codebase, and I also plan to have the development team run the scanner before pushing their work. Aside from the initial effort to set this up, it's pretty much zero effort for a lot of benefit.

Static code analysis shouldn't replace manual code reviews though - an automated tool is configured to look for specific code/markup that indicates problems. A developer carrying out a code review understands the purpose of the code and can determine if it looks like it does what it is supposed to. Code reviews are also an excellent way to share knowledge in a development team. By adding automation you free up code reviewers from having to check whether every variable is used and figure out what makes a method too complex. By using both you'll catch more problems earlier, so what's not to like?

Resources