3

Ideally, I'd like to write a unit test to help me "dummy check" for unexpected effects of the upcoming guest user sharing setting changes.

We'd had some controller/trigger code "gracefully handling" data it couldn't see thanks to the new sharing restrictions ... which resulted in simply "not doing" some work that it was supposed to do, which from a business process perspective isn't "graceful" at all (it was an important automation for downstream processes).

I'd like to write up a unit test with some system.assert statements validating that "data still goes where it's supposed to" when the "submit" button a public-facing Visualforce page of a community is clicked.

Unfortunately, I can't seem to make the "submit" button on a Controller for a public-facing Visualforce page work in the context of a unit test because the record whose creation it results in doesn't get its corresponding "sharing setting" record created in time for the execution context to pick it up.

I did a little proof of concept here ...

@isTest
public class TestClass {

    private static Id guestProfileId = Util.getProfileIdByName('Public Facing Portal Profile');
    private static User guestUser;
    private static Id portalGroupId = [SELECT Id FROM Group WHERE Name = 'Public_Portal' LIMIT 1][0].Id;

    private static Custom_Object__c co;


    private static testMethod void runTest() {
        createCO();
        Test.startTest();
        // Note that putting createCO() inside the test and doesGuestSharingRuleExistForCO() doesn't help.
        // That's what I was afraid of.
        try {
            doesGuestSharingRuleExistForCO(co.Id);
        }
        catch (Util.DeveloperException e) {
            System.assert(FALSE, e.getMessage());
        }
        Test.stopTest();
    }


    private static void cacheUsers() {
        guestUser = [SELECT Id FROM User WHERE profileid = :guestProfileId AND IsActive = TRUE LIMIT 1][0];
    }

    private static void createCO() {
        co = new Custom_Object__c();
        INSERT co;
    }

    private static void doesGuestSharingRuleExistForCO(Id coId) {
        List<SObject> existingSharingRulesOnCO = [
            SELECT Id FROM Custom_Object__share
            WHERE RowCause = 'GuestRule' AND AccessLevel = 'Read' AND ParentId = :coId AND UserOrGroupId = :portalGroupId LIMIT 1
        ];
        if ( existingSharingRulesOnCO.isEmpty() ) { throw new Util.DeveloperException('Guest User sharing is not on for this custom object ID'); }
    }

    private static Boolean isCOSharingFlippedOnAtAll(){
        String lookingFor = 'Custom_Object__Share';
        for (Schema.SObjectType sObjectType : Schema.getGlobalDescribe().values() ) {
            String sobjName = String.valueOf(sObjectType);
            if (sobjName.contains(lookingFor) ) { return TRUE; }
        }
        return FALSE;
    }

}

Am I just out of luck? Is there no way to properly unit-test the effects (to automations relying upon "guest user" sharing settings when running as "guest user") of simulating the insertion of records into my database from a Visualforce page, due to not being able to separate out "execution context" enough and "wait" for the sharing rule record for the new record to come into existence?

3

Set up records that need sharing in @testSetup. The share records will be visible for all unit tests after @testSetup completes.

By using @testSetup, things that occur "after the transaction", such as record sharing, are created. Tests that use those records will then have access to those records.

Confirmation

@isTest  class q293720 {
    @testSetup static void setup() {
        insert new Account(Name='demo');
    }
    @isTest static void test() {
        System.assertNotEquals(0, [select count() from accountshare]);
    }
}

In my Developer Org, this test passes when setup() is uncommented, and fails when commented. This proves that sharing records are created.

| improve this answer | |
  • N.B. This was edited after I tested that this behavior does work in unit tests. – sfdcfox Feb 6 at 20:35
  • Weird -- I just moved the call to createCO(); to a new @testSetup and then added a little co = [SELECT Id FROM Custom_Object__c LIMIT 1][0]; to the first line of the main unit test since static variables don't "remember" their values between test setup and main-test ... but doesGuestSharingRuleExistForCO() still throws an error. – k.. Feb 6 at 21:26
  • wait - what? @testSetup creates Field History records? I just learned something new. There a quite a few SFSE answers that need revising on this topic – cropredy Feb 7 at 1:57
  • @cropredy That was a mistake. Shares are created, but field history is not, as far as I can tell. That'd solve so many problems if it did. – sfdcfox Feb 7 at 2:52
  • This approach appears to work for standard sharing rules, but it doesn't work for the new guest user sharing rules released in early 2020. – geeljire Apr 28 at 13:17
2

Quoting a section from this help topic: https://help.salesforce.com/articleView?id=security_sharing_rule_types.htm&type=5

You can’t use Apex to create a criteria-based sharing rule. And you can’t test criteria-based sharing using Apex.

I'm assuming the new Guest User Sharing rules fall under this bracket and therefore can't be used in Unit Testing. After running into my own brick wall with this, it appears any Share records created are for the Record Owner, or Parent Owner (Owner/Implicit Child).

Looks like manual testing is the way to go for ensuring Secure Guest User Access doesn't break Production.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.