1

Looking at bringing in a selector pattern to help reduce a lot of cumbersome test code. But as I refactored, I noticed that after introducing a selector class, the existing test will fail when it uses System.runAs(). But passes when I comment it out. I would like to understand more about why this is happening.

The class that makes the query is declared as with out sharing and the selector is declared with inherited sharing.

Just to double check I gave every profile in my SB access to the selector class and the base class.

I also updated the selector to run in System.mode()..

public  without sharing class AccountSelector extends fflib_SObjectSelector {

    public AccountSelector() {
    super(false, DataAccess.SYSTEM_MODE);
}
    public Schema.SObjectType getSObjectType() {
        return Account.SObjectType;
    }
}

Here is the method I want to test, it gets one Account and serializes it in a wrapper class for the presentation layer. When tested with System.runAs() it fails to return data.

public static string initializeAccountForm(String accountId) {
    AccountSelector accountSelector = (AccountSelector)EMEA_Application.selector.newInstance(Account.SObjectType );
        Account accountList = (Account) accountSelector.selectSObjectsById(new Set<Id>{accountId})[0];

            return JSON.serialize(
                new InitContactFormReturn(
                    accountList, 
                    PortalAccountController.getCountryCodePicklist(),
                    PortalAccountController.getPreferredLanguagePicklist()
                )
            );
        }

I don't want to include all the test code because there 100 line of set up to test this method. How could I debug this to figure out what the issue is with System.runAs in this case?

This is the test method with out any mocking. I would like to have a green test before I refactor further with mocks.

@isTest
    static void initializeAccountFormTest(){
        //Arrange
        String serializedResponse;
        GcxManageAccountController.InitContactFormReturn parsedResponse;
        Account newlyAddedAcct = [
                SELECT id, Salutation, FirstName, LastName, DateOfBirth__pc, LanguagePreference__pc, PersonMailingCountry, PersonMailingState, 
                PersonMailingStreet, PersonMailingCity, PersonMailingPostalCode, PersonEmail, PhoneNumberCountryCode__pc,
                Phone, PrimaryPhoneType__pc, SecondaryPhone__pc, SecondaryPhoneType__pc, ShippingCountry, ShippingStreet, ShippingCity, ShippingState, ShippingPostalCode
                FROM Account
                WHERE PersonEmail = '[email protected]' 
                LIMIT 1
                ];

        //Act
        AccountSelector selector = (AccountSelector)EMEA_Application.selector.newInstance(Account.SObjectType);
        System.debug('system mode' + selector.selectSObjectsById(new Set<Id>{newlyAddedAcct.Id})[0]);
        Test.startTest();
        System.debug('user details are ' + standardTestUser);
        System.runAs(standardTestUser){
            String result = GcxManageAccountController.initializeAccountForm(newlyAddedAcct.Id);
            parsedResponse = (GcxManageAccountController.InitContactFormReturn)JSON.deserialize(result, GcxManageAccountController.InitContactFormReturn.class);
       }
        Test.stopTest();

        system.assertEquals(newlyAddedAcct.PhoneNumberCountryCode__pc, parsedResponse.Account.PhoneNumberCountryCode__pc);
        system.assertEquals(PortalAccountController.getCountryCodePicklist().size(), parsedResponse.phoneCountryCodeOptions.size());
    }

Below is the query string that the base class is generating.

string representation SELECT id, dateofbirth__pc, firstname, languagepreference__pc, lastname, loqateupdateverification__c, loqateverifiedmailing__pc, loqateverifiedshipping__c, personemail, personmailingcity, personmailingpostalcode, personmailingcountry, personmailingstreet, personmailingstate, phone, phonenumbercountrycode__pc, primaryphonetype__pc, salutation, secondaryphone__pc, secondaryphonetype__pc, shippingcountry, shippingstreet, shippingcity, shippingpostalcode, currencyisocode FROM Account WHERE id in :idSet WITH SYSTEM_MODE ORDER BY CreatedDate ASC NULLS FIRST

And here is all the code from the testSetup method that defines the users

@TestSetup
    static void makeData() {
        Id runningUserRoleId = [
            SELECT Id
            FROM UserRole
            WHERE DeveloperName = 'Insulet_Super_User_View_All'
            LIMIT 1
        ]?.Id;
        Id runningUserProfileId = [
            SELECT Id
            FROM Profile
            WHERE Name = 'Insulet System Administrator'
        ]
        ?.Id;

        User runningUser = TestDataFactory.createUser(
            'Admin',
            'User',
            '[email protected]',
            'en_US',
            runningUserRoleId,
            runningUserProfileId
        );

        insert runningUser;

        System.runAs(runningUser) {
            Id testUserProfileId = [
                SELECT Id
                FROM Profile
                WHERE Name = 'Insulet External Customer'
                LIMIT 1
            ]
            ?.Id;

            insert TestDataFactory.createDataUsePurposes();
            Account createdAccount = TestDataFactory.createAccount(
                customerAccountRecordTypeId,
                'Mr',
                'Test',
                'User',
                Date.today().addDays(-100),
                '1234567890',
                '1234567890',
                'Work',
                'Mobile',
                testCustomerUserEmail,
                'Ireland'
            );
            Account accountToAdd = TestDataFactory.createAccount(
                customerAccountRecordTypeId,
                'Mr',
                'firstName',
                'lastName',
                Datetime.now().date().addDays(-100),
                '5555555555',
                '5555555555',
                'Work',
                'Mobile',
                '[email protected]',
                'Ireland'
            );

            List<Account> acctList =  new List<Account>{createdAccount, accountToAdd};
            List<Account> acctUpdateList = TestDataFactory.updateAccountsWithPrimaryPhysican(
                acctList
                , 'ProviderTestolaAccName'
                , 'ProviderTestolaContName'
                , '[email protected]'
            );
            insert acctList;
            createdAccount = [
                SELECT
                    FirstName,
                    LastName,
                    PersonBirthdate,
                    Phone,
                    PersonEmail,
                    PersonContactId
                FROM Account
                WHERE Id = :createdAccount.Id
            ];

            User customerPortalUser = TestDataFactory.createPersonAccountUser(
                createdAccount.FirstName,
                createdAccount.LastName,
                createdAccount.PersonEmail,
                'en_US',
                null,
                testUserProfileId,
                createdAccount.PersonContactId
            );

            insert customerPortalUser;
        }
    }
3
  • 1
    how is standardTestUser defined? in debug, what SOQL is generated by selectSobjectsById ?
    – cropredy
    Commented Feb 27, 2023 at 18:58
  • @cropredy I was hoping you might chime in. I edited my question with the query string and the test setup code. Commented Feb 27, 2023 at 19:12
  • Unless I am missing something else, I would question the value of testing in user context when all the original code with was without sharing and did not enforce crud or FLS Commented Feb 27, 2023 at 19:16

1 Answer 1

1

Two things to look at

  1. accountSelector.selectSObjectsById(new Set<Id>{accountId})[0];. This actually runs with sharing because it is a method in the super class fflib_SobjectSelector, it is not a method in your AccountsSelector.cls. I personally never use this method, instead, use a method selectById in the selector

Side note: there's a long thread on sharing in selectors in the GitHub for apex-common

  1. If the test user relies on criteria-based sharing rules to gain visibility to Account, note that Apex test methods don't honor criteria-based sharing rules (at least last time I checked). The work around is to mock your selector to return what the user would normally see if criteria-based sharing rules were operating.
1
  • Great answer. I was wondering about using that method from the base class. I should have looked harder. Commented Feb 28, 2023 at 13:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .