Sfdx-git-delta: Make List of Apex Class Names from Delta Package.xml to Use In Delta Deployment with Test Run

Created on 12 Apr 2021  路  6Comments  路  Source: scolladon/sfdx-git-delta

### Is your proposal related to a problem?

It is fantastic, that the SGD generates a delta package.xml, which is used to deploy the delta. However, if the delta package.xml file contains apex classes (and apex test classes), there is no easy way to generate a list of apex classes to use for the RunSpecifiedTest part of a validation run

If the SGD plugin also could generate such a list, it would make it so easy to also run selected tests in a validation run, further enhancing the delta deployment experience.

E.g. the command for running specified tests:
sfdx force:source:deploy --testlevel RunSpecifiedTests --runtests 'MyApexClass1, MyApexClass1_Test, ... '

### Describe the solution you'd like

If the SGD command generates the following package.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>MyApexClass1</members>
        <members>MyApexClass1_Test</members>
        <members>...</members>
        <name>ApexClass</name>
    </types>
    <version>51.0</version>
</Package>

Then I want the SGD plugin to also generate a .txt file (e.g. ApexClassNames.txt) or return a string. The file/string should contain the names of apex classes in above package.xml file (both normal classes and test classes), separated by commas.
E.g. content is string with value MyApexClass1, MyApexClass1_Test, ...

If .txt file, it can be placed in a new folder, name is optional, to be decided.

### Describe alternatives you've considered

The alternative is to generate a script, which gets the package.xml file from the SGD method, access the type with name ApexClass and retrieve all members from the section. Not the prettiest solution, and script will vary between each CI/CD developer, who has this need.

enhancement

Most helpful comment

Deciding exactly which tests to run with --testlevel RunSpecifiedTests can be a tricky business: some teams will run only the new and modified classes, other teams will rely on their test naming convention to list the tests classes to execute, some other teams will have a baseline of test classes that are run under certain circonstances (_"if something is changed on Account, always run AccountTriggerHandler_Test"..._)
So I feel that if we start offering an output of the test classes to run in SGD, there will be an expectation that we support those (or some of those) different strategies. It would be great in the long-term, but would definitely require more work.

So, If we "only" need the list of Apex classes from the package.xml, using a tool such as yq, as @scolladon suggested, seems to me like that the best short-term solution. Here is an example of a command to output the list of Apex classes:
cat package/package.xml | xq . | jq '.Package.types | select(.name=="ApexClass") | .members | join(",")'

_EDIT: a more robust version would be cat package/package.xml | xq . | jq '.Package.types | if type=="array" then .[] else . end | select(.name=="ApexClass") | .members | join(",")'_

@nickytorstensson Would that solution meet your need?
(note that you can also add additional transformation before the | join(","), if needed)

All 6 comments

Hi @nickytorstensson !

Thanks for this great suggestion.

I open the solution debate. Should we

  • Add this output in the current sgd:delta:generate command ? Then should it be triggered via a specific parameter or in all case ? How to drive the output ? Both in a file and in the json output ?
  • Create another command to do it from a package.xml list. So it can be reused in other context
  • Document a xpath transformation using yq

Hi @scolladon

  1. I think, since the enhancement is relevant for every execution of the sfdx sgd:source:delta command, it should be part of that current command. I don't foresee a need for additional parameters. I think it would be best to have the file generated, containing the list. Additionally returning the output in JSON could be relevant too, but would not be relevant in my scenario (could be a future change request).
  2. I understand it would be good with a separate function for single use, but I would assume that implies that the package.xml is modified after the sfdx sgd:source:delta command, which I don't expect (but of course could be relevant in some edge cases).
  3. I am not familiar nor experienced with yq / jq, so I will rely on your experience on that.

I hope my answers help :-)

Deciding exactly which tests to run with --testlevel RunSpecifiedTests can be a tricky business: some teams will run only the new and modified classes, other teams will rely on their test naming convention to list the tests classes to execute, some other teams will have a baseline of test classes that are run under certain circonstances (_"if something is changed on Account, always run AccountTriggerHandler_Test"..._)
So I feel that if we start offering an output of the test classes to run in SGD, there will be an expectation that we support those (or some of those) different strategies. It would be great in the long-term, but would definitely require more work.

So, If we "only" need the list of Apex classes from the package.xml, using a tool such as yq, as @scolladon suggested, seems to me like that the best short-term solution. Here is an example of a command to output the list of Apex classes:
cat package/package.xml | xq . | jq '.Package.types | select(.name=="ApexClass") | .members | join(",")'

_EDIT: a more robust version would be cat package/package.xml | xq . | jq '.Package.types | if type=="array" then .[] else . end | select(.name=="ApexClass") | .members | join(",")'_

@nickytorstensson Would that solution meet your need?
(note that you can also add additional transformation before the | join(","), if needed)

I agree with @mehdisfdc 's comments and offer my own solution as an example for discussion.

@nickytorstensson, I am using SGD for the same use case as you've described. I use the --generate-delta option to create a folder of changes in the build directory, then use a simple PowerShell script to find the test classes. Something similar could be done with Python or your scripting language of choice.

#(very basic PowerShell example)
$files = Select-String -Path "*Test.cls" -Pattern "isTest","testMethod" -List | Get-Item
foreach ($f in $files) {
    $testList += $f.BaseName + ","    
}

I use this $testList string as the input to the RunSpecifiedTests option.

As our testing pipeline matures, we will be selecting tests much like @mehdisfdc suggests: some baseline suites, added/updated tests, or possibly even detecting dependencies in code and running related tests so the usefulness of SGD outputting test class names will diminish.

Hi all,

First, thanks for sharing your input and considerations!

I agree, that every team has their own conventions for selecting, which test classes need to run. And that definitely makes it difficult to develop something general, if everyone will not be using it the same way. However, my suggestion does not focus on the individual team's needs, but the general need, which I believe should apply to all teams.

  • As an argument for getting all added or modified apex classes:
    In the context of an apex class; if you as a developer modify it, then (in principle) you should also update your test class to accommodate for the changed logic. As such, I think it is still valid to include all added or modified apex classes for a RunSpecifiedTest run, and this would be a general use case for any team (in my opinion).

  • Countering my own argument:
    By example; though fixing typos and simple refactoring will likely not change the test class, it would in principle be redundant to modify the test class by just adding a line break or space for the sole purpose of getting it included.
    I know, the workaround in this example introduces an extra step, but for a small cost.

@mehdisfdc, considering that introducing such functionality will likely result in expectations for extended functionality, then I also agree with scolladon, that this should be in a separate command, such that it can scale well.

On your examples for getting the apex classes into a variable, I would have to look into that (still a novice user ;-), so would have to install the programs on my Docker image and run some tests)

Best, Nicky

Hi all,

I personally prefer the solution using yq.
Because

  • it is very simple
  • it relies on a very trusted tool
  • it is simple to use in local and in CI/CD as well
  • it fulfils the requirement (do the job)
  • it is flexible enough to be adapted to fit the most demanding use cases
  • it allows the plugin to not reinvent the wheel and to stay focus on what it does best.

I propose to document the usage of yq in the README.md using the query designed by @mehdisfdc to show case how to run Specified Test using this method.
It could be a good (crawl) answer to the requirement.

Was this page helpful?
0 / 5 - 0 ratings