Testing Custom Permissions

Testing Custom Permissions

Table of Contents:

Intro: Feature-based Code Routes

Getting our first custom permissions test to pass, working with setup objects in apex tests, wrapping up testing custom permissions.

Custom Permissions changed the game when it came to creating programmatic checks for feature management within Salesforce. Between Custom Metadata and Custom Permissions, Salesforce as a whole has been trying to gently move people away from permissions management by way of hierarchical custom settings (or, even worse, iterating through Permission Sets!). And there’s a lot to love when it comes to Custom Permissions. Since Winter ‘18, the FeatureManagement.checkPermission method has enabled developers to easily implement permission-based code routing. However … when it comes time to testing feature-flagged code, how can we easily ensure that our tests remain isolated without polluting our test domain (or, even worse, unnecessarily exposing private methods merely to test the innards of a class)? Join me on the journey toward testing Custom Permissions painlessly!

Let’s say we have a business requirement that asks for a task to be created based off of Opportunity owners when an API interaction from an external system identifies outreach as the next best step. This integration could be the result of Opportunity Stages being updated by a Sales person; it could be kicked off by an internal cron job; it could come from anywhere. The business would like to gradually roll this feature out to users without fully opting all of them in at once. This is the perfect use-case for Custom Permissions: we can feature flag the logic that creates the task, and opt users from Sales in as we please from a Permission Set with the Custom Permission included:

And the Permission Set:

Some example code, based off the premise that an update to an Opportunity triggers this action. It could be done synchronously, through a trigger handler , or asynchronously, through a Queueable or Batch job . We’ll start with the test for the happiest path:

We’ll get started on that OpportunityTaskHandler object in a second; for now, of course, the test fails with the classic:

Perfect. You’ll note that we are actually getting an additional safety feature right out of the box; because there is no LIMIT command on the SOQL query for createdTask , we’re also safe-guarding against future regressions where multiple Tasks might be introduced. With the advent of the Winter ‘21 release, you’ll also note that next week we will be able to take advantage of the Safe Navigation feature to perform the same query:

Of course, such syntax sugar only avails us in the event that we only need to assert for one thing, but I point it out here in the event that you haven’t checked the release notes recently.

Right now we have a failing test, but we also have zero functionality and no Custom Permissions wired up yet. Let’s fix that:

We’ll use a public static String for the Task Subject to aid in testing, but you could just as easily use a Custom Label. The only other design decision to talk about is the routing — the reference to the Custom Permission itself. In a more complicated ask, and a more sophisticated system, you might also choose to use some form of configuration or metadata to inject the name of the Custom Permission being used; instead of hard-coding Is_API_Task_Creation_Enabled , you’d have the ability to swap Custom Permission(s) dynamically. James Hou has several interesting POCs on how this might be accomplished — while these feature-flag systems are not production ready, looking through the patterns in that repo might help you in your own search for best practices regarding customizations like this. But I digress — back to it.

We’ve got our functionality — let’s get back to our test! One thing we can do is validate that the test has been setup correctly before touching anything else:

One of a few things I’m not thrilled with concerning the checkPermission method? It should throw an exception, in my opinion, if you pass in a Custom Permission name that doesn’t exist. It doesn’t do that. This is one of the other reasons I brought up the feature-flagging framework, above — it’s important that you isolate and minimize String-based parameters, both in your tests and in production-level code. It’s too easy for misspellings to go unnoticed, especially if you aren’t giving yourself the safety net that tests represent. Though it consumes an extra SOQL call, there is some wisdom to be gained in wrapping the checkPermission method to validate that the Custom Permission in question actually exists … for the moment, we’ll hold off on implementing something like that.

Anyway. The test is still failing. Let’s address that. One possible way to do so — and the method we’ll employ first — is to assign the Permission Set featuring the Custom Permission to our test user. There are ample pitfalls to this approach — which we’ll cover shortly — but we’re head’s-down in the “red, green, refactor” TDD methodology at the moment, and the only thing that matters presently is getting that test to pass.

Permission Sets are metadata; they’re retrievable in our tests without having to use the ‘seeAllData` test attribute (and you shouldn’t be using that attribute anyway). If you aren’t familiar with how Users are assigned to Permission Sets within Apex, the process is refreshingly simple:

Yes! Writing Apex is fun and easy! With any luck, we’ll be done with this requirement before lun—

Wait, what. Why is there still no Task being created? Is there some kind of async process surrounding permissions that is preventing the call to FeatureManagement.checkPermissions from returning true? Sure enough, debugging shows the value has not changed even after the Permission Set has been assigned. Well, that’s OK — we’re veterans of async deception in Apex, which means we know wrapping this thing in Test.startTest / Test.stopTest should force all async actions — including the presumed permissions updating — to complete. I’m thinking maybe I’ll have a caprese sandwi—

Hmm. OK, that … didn’t work. I didn’t expect that. What about if we wrap the calling code in System.runAs ? Even though we’re already running the test as ourself, maybe there’s something about running the test in another context that will help:

I’ll spare you the drama — the test is still failing. I’m recreating this experience, step-by-painful-step, as it happened to me when I first went to work on a feature like this. When it was happening to me, in the moment, I’ll admit — I was tempted to give up. I already had a test passing that verified the objects in question (which were not Opportunities for the project I was working on, but the same concept applies) were being filtered correctly. I knew the project’s code coverage was high enough that a few untested — and admittedly simple — lines were unlikely to arouse suspicion or red flags. But that’s not the Joys Of Apex way. Indeed, the thought of giving up so galled me that I was driven to continue. Deeper into the mysterious SObjects known as “Setup Objects” we will have to go …

There are quite a few objects that belong to the “Setup Object” category, which becomes relevant to us since we would like to both manipulate these objects and perform other DML (the Task insertion) within our test. We are typically spoiled when it comes to documentation on the SFDC platform, and this is no exception . Here are some of the more pertinent objects that I’ve run into which can generate the dreaded “mixed DML” setup object error when writing unit tests:

  • PermissionSet
  • PermissionSetAssignment
  • ObjectPermissions (but, confusingly, not FieldPermissions ?)
  • SetupEntityAccess

It’s that last one — SetupEntityAccess — which will prove crucial to aiding and abetting our unit tests. It turns out that in addition to the PermissionSetAssignment object, which is still required, we also need to ensure that our Permission Set is correctly set up with the reference for the Custom Permission in order for our test to work. This also forces us to make our test fully independent from the Permission Set that we’ve created — which is great. Since Permission Sets can be changed without running all tests, it’s possible to remove the Custom Permission we’ve created from our Permission Set and deploy without anybody being the wiser — until our unit tests are run the next time a code update is deployed! We’ll remove that possible failure point from our codebase and enjoy clean code in the process.

I’ll also mention that even after all of this was pieced together, I still had to do the Test.startTest() / Test.stopTest() song and dance prior to finally having success with just the plain System.runAs(user) method — only in the runAs context is a User’s Custom Permission status successfully re-calculated during testing!

Here’s what creating the full list of objects necessary to tie everything together looks like:

Putting it all together, our test now looks like:

Now we get a different error:

This is because the Ids generated by TestingUtils aren’t recognized by the database as valid — because the given Account and Contact records do not exist. This is where dependency injection / use of the Stub API comes into play, going back to the Mocking DML article :

And in the test:

Note that the singleOrDefault method throws if more than one element is present — the same as our old SOQL query safe-guard. Excellent. And the test passes! But maybe you’re more into the Stub API these days? This is a great chance to plug Suraj Pillai's UniversalMock Stub API framework for easy stubbing. There is one critical limitation with the Stub API, however — you can’t mock private methods.

This means that for mocking DML, you’re still “stuck” having a DML wrapper of sorts — which would then allow you to use the mock like so:

For mocking DML, I find the use of the Stub API a bit heavy (that’s a lot of casting!), but it’s good to point out its flexibility to people who may not be aware that a whole host of other options are available to them when testing complicated objects.

We’ve successfully decoupled our tests from any one Permission Set existing, and also shown how to test for the existence of Custom Permissions in isolation. The negative test — simply verifying that no Task is created if the User does not have the Custom Permission enabled — is left as a trivial exercise for the reader. The more serious task would be wrapping calls to FeatureManagement , as mentioned earlier, to validate that the Custom Permission exists — you can afford the extra SOQL call, hopefully, but this also makes the method non-bulk-safe.

Anyway, we’ve planted the seed for extensible permissions-based routing. I don’t have the answer for how to best dynamically gate functionality … at the moment, I would probably go the route of Custom Metadata being fed into a system calling FeatureManagement.checkPermission , with sensible defaults. One problem with the dynamic version of feature flagging is that it puts the onus on the business as a whole to eliminate dead code routes when certain features are deprecated; if your tests are properly self-isolating, the only way you would know that code was no longer reachable would be if somebody went and deleted the Custom Permission in question … otherwise, if it hangs out, without being assigned to any Permission Set, you have no intuitive, in-system, way to validate a feature being deprecated.

Despite this dead-code issue, I hope that I’ve given you plenty to think about when it comes to Custom Permissions. Worst case scenario, I’m simply confirming what you already know — Custom Permissions play nicely within Apex; you just need to be sure your tests are properly decoupled. I’ve uploaded the example code if you want to browse through on Github — till next time!

If you enjoyed this article, you might also enjoy the next one in the series — we explore the idiosyncracies and mismatches between Date fields in Formula Date Issues .

  • ← The XY Problem In Programming
  • Formula Date Issues →

Dive into the Summer ’24 Release

As an admin, you can help your organization succeed by staying up to date with the latest Salesforce releases and getting familiar with product enhancements.

Check out the Summer ’24 release notes for a list of new features and available updates.

Summer_24_Desktop_yr_V2.png

Did you find this helpful?

Help us enhance your experience.

banner footer image

Document Generation Error- "List has no rows for assignment to SObject "

  • Make sure the affected user has ‘Salesforce CRM Content User’ enabled. This must be checked on their User Detail page in Setup.
  •  The affected user should be provided with  access to the Docgen Document Template Library. To enable users to access the files in the Document Template Library, you must add them as members. Or, you can create a group, grant access to the group, and add users to that group.
  • Make sure to deactivate and reactivate the Document Template
  •  If there are custom fields in the Content Version object, marked as required, the error occurs. The managed package code will not update custom fields and they need to be marked required only at the Page Layout level.
  • For DOCX Templates, the ContentVersionId will be set for the affected Document Template. Make sure this record is shared with the Docgen Document Template Library to avoid any errors.
  •  Open the active template in the Document Template tab (NOT the Docgen Document Template
  • Add ContentVersionId field to the layout if it is not present already
  • Go to the Id in Salesforce to check Sharing Settings.
  • If the ContentVersionId file is not shared, share it manually with the library/user.

Company Logo

Cookie Consent Manager

General information, required cookies, functional cookies, advertising cookies.

We use three kinds of cookies on our websites: required, functional, and advertising. You can choose whether functional and advertising cookies apply. Click on the different cookie categories to find out more about each category and to change the default settings. Privacy Statement

Required cookies are necessary for basic website functionality. Some examples include: session cookies needed to transmit the website, authentication cookies, and security cookies.

Functional cookies enhance functions, performance, and services on the website. Some examples include: cookies used to analyze site traffic, cookies used for market research, and cookies used to display advertising that is not directed to a particular individual.

Advertising cookies track activity across websites in order to understand a viewer’s interests, and direct them specific marketing. Some examples include: cookies used for remarketing, or interest-based advertising.

Cookie List

IMAGES

  1. Salesforce: Visualforce System.QueryException: List has no rows for

    system.queryexception list has no rows for assignment to an object

  2. System.QueryException: List has no rows for assignment to SObject

    system.queryexception list has no rows for assignment to an object

  3. System.QueryException: List has no rows for assignment to SObject for

    system.queryexception list has no rows for assignment to an object

  4. Getting "System.QueryException: List has no rows for assignment to SObject" error with custom

    system.queryexception list has no rows for assignment to an object

  5. Salesforce: Testmethod problem: System.QueryException: List has no rows

    system.queryexception list has no rows for assignment to an object

  6. Trailhead : Apex Integration Services System.QueryException: List has

    system.queryexception list has no rows for assignment to an object

VIDEO

  1. Salesforce: System.QueryException: List has no rows for assignment to Sobject Error?

  2. Salesforce: System.QueryException: List has no rows for assignment to SObject (3 Solutions!!)

  3. System.QueryException: List has no rows for assignment to SObject

  4. An Apex error occurred: System.QueryException: List has more than 1 row for assignment to SObject

  5. Two (02) Ways To Fix [Illuminate Database QueryException]

  6. Salesforce: System.QueryException: List has no rows for assignment to Sobject Error?

COMMENTS

  1. List has no rows for assignment to SObject

    If you get more than 1 row and attempt to assign that to your Accountx variable you will get the opposite of your original problem - System.QueryException: List has more than 1 row for assignment to SObject! So one way of guarding against this would be to use Account accountx = [SELECT Id FROM Account LIMIT 1]'. - frup42.

  2. apex

    Or maybe it's being passed but it's useless. If you have StandardSetController the Id value in the page URL might be used as filter id (listview), not an id of any particular record. Meaning query will still return 0 rows. What were you trying to do? Maybe just removing that part of condition will help although this list can grow very fast.

  3. Apex error 'List has no rows for assignment to SObject'

    What's not obvious is that it also assumes that exactly one row is returned! Although this is unlikely to occur for Contact, it is highly likely to occur for any custom objects you create, especially when a WHERE statement is used that might return zero rows, such as:

  4. System.QueryException: List has no rows for assignment to SObject

    First, the 'account' instance must be initialised. Second, just make sure the ID is being passed in the page's URL correctly. In the ApexPages.currentPage ().getParameters ().get ('id') statement, the .get ('id') part is case sensitive. So please make sure, that is how you have passed your ID in the URL. Third, there should be a record in ur ...

  5. Error 'List has no rows for assignment to SObject' in Salesforce CPQ

    This issue can also occur when the user attempts to reconfigure a Primary Quote without Read access to the associated Opportunity. Lack of access to the Opportunity object throws List has no rows for assignment to SObject' since our code queries for the Primary Quote on that opportunity.

  6. Joys Of Apex: Testing Custom Permissions

    System.QueryException: List has no rows for assignment to SObject Perfect. You'll note that we are actually getting an additional safety feature right out of the box; because there is no LIMIT command on the SOQL query for createdTask , we're also safe-guarding against future regressions where multiple Tasks might be introduced.

  7. apex

    System.QueryException: List has no rows for assignment to SObject on test class 0 Test Class Fail: Batch Class: System.QueryException: List has no rows for assignment to SObject

  8. Document Generation Error- "List has no rows for assignment to SObject

    Make sure this record is shared with the Docgen Document Template Library to avoid any errors. Steps to fetch ContentVersionId: Open the active template in the Document Template tab (NOT the Docgen Document Template. Add ContentVersionId field to the layout if it is not present already. Go to the Id in Salesforce to check Sharing Settings.

  9. "System.QueryException: List has no row

    A user receives a reply from the system after replying to an approval email. The system reply says the following: "The attached email could not be processed because the Apex class sbaa.ApprovalEmailHandler failed. System.QueryException: List has no rows for assignment to SObject Class.sbaa.ApprovalDAO.loadById: line 5, column 1

  10. Error when rejecting Applications: List has no rows for assignment to

    Applications (and the other AMS Stages) has a Master-Detail Object Relationship within the Salesforce Data model to Job Order. This means the Record Type of the AMS Stages is acquired from the Job Order.

  11. Why do I see a "System.QueryException: List has no rows for assignment

    caused by: System.QueryException: List has no rows for assignment to SObject (sbxe1)" Potential Solution. This issue tends to be related to missing record types on the Opportunity and Lead objects. As a starting point, ensure that the following record types are included. Opportunity object: Soapbox Registration Order

  12. System.Query Exception: List has no rows for assignment to s-object

    Salesforce has been coming up with new certifications every now and then to evaluate and test the knowledge of individuals on different functionalities and features… Apex , App Builder Training , App Development Lifecycle , app-builder-in-salesforce , Application Securities

  13. FATAL_ERROR|System.QueryException: List has no rows for assignment to

    I am getting FATAL_ERROR|System.QueryException: List has no rows for assignment to SObject. I know that it is because it is not returning any rows in the SOQL query but what I want is that if it returns any row, it should update ALO field on contact object, if it is null then ALO field should be set to null. Here is my code:

  14. Getting an error when tried to execute workbook apex code

    "System.QueryException: List has no rows for assignment to SObject: Class.InvoiceUtilities.renumberLineItems: line 8, column 1 AnonymousBlock: line 1, column 1 AnonymousBlock: line 1, column 1"

  15. System.QueryException: List has no rows for assignment to SObject

    System.QueryException: List has no rows for assignment to SObject Question Share ... "List has no rows for assignment to sObject," so my solution is to leave it as a list and check if it's empty. Reply reply ... XML, etc.), REST APIs, and object models. PowerShell includes a command-line shell, object-oriented scripting language, and a set of ...

  16. System.QueryException: List has more than 1 row for assignment to SObject

    Presumably the value sim is of type Car__c.You cannot assign the result of a query that returns anything other than exactly one record to a value whose type is an SObject. In particular, this does not make sense when you're performing a search query with a LIMIT 20 clause.. You should type your variable as a List<Car__c>.