6

I am using the UserManagement class to register and login users with just their Email or Phone Number, which I have managed to get working very nicely...

I have now also built a small LWC to allow user to Register and Deregister an identifier. Which works nicely also...

My issue is how on earth do I test this?!

To check whether something has been Registered or is still Unregistered I am querying the TwoFactorMethodsInfo sObject to return back both the HasUserVerifiedMobileNumber and HasUserVerifiedEmailAddress, which allows me to update the UI to display different buttons, etc...

So in a test class when I call this method which is simply:

@AuraEnabled(cacheable = true)
public static TwoFactorMethodsInfo getVerifiedInfos(){
    // Confrim the User has a Verified Email Address and is able to login
    TwoFactorMethodsInfo verifiedInfos = [SELECT HasUserVerifiedMobileNumber, HasUserVerifiedEmailAddress
                                            FROM TwoFactorMethodsInfo
                                            WHERE UserId = :UserInfo.getUserId()];

    return verifiedInfos;
}

It throws the following exception:

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

So I decided to set SeeAllData to true (I had to fight the urges to the dark side after adding this), ran my test class again and then I got:

System.UnexpectedException: common.exception.SfdcSqlException: We encountered an unexpected error. Contact Salesforce Customer Support. Error ID: 334613542-444174 (290339055)

So moving on further through my class, I thought I'd wait and deal with the headache later, surely I can run tests on the System.UserManagement.initRegisterVerificationMethod() or the System.UserManagement.deregisterVerificationMethod?

I removed the devil that is SeeAllData = true and continued on my journey to discovery. I added both methods into the Test Class, commenting out the beast above, surely this will work?

No... initRegisterVerificationMethod threw:

Failed to get network info.

With SeeAllData = true

Failed to get network info.

Errors... Again...

Let's try the System.UserManagement.deregisterVerificationMethod, who can guess the outcome?

Well no you were wrong... because even in a try/catch it still somehow threw an Exception that bypassed my try/catch:

System.UnexpectedException: Salesforce System Error: 1266895027-432990 (-1126684175) (-1126684175)

This is when I thought to myself: why am I being silly... surely I should create a user first using the UserManagement.initSelfRegistration method!

Failed to get network info.

Has ANYONE actually been able to test ANY of this? Or will I have to do something horrendous to actually cover any lines of code in this class?

  • Those error codes like 1266895027-432990 (-1126684175) are GACKs. Ideally they would be raised with Salesforce support so better error handling can be added to the internal code. – Daniel Ballinger Dec 3 '19 at 18:39
6
+25

I think the sad truth is that there is currently (as at v47.0) no support in Apex for testing those methods or the corresponding TwoFactorMethodsInfo API. I'm asking around for verification, but the docs don't provide any clarification or consideration for how to test them.

Since @IsTest(SeeAllData=true) isn't a good idea, I'd recommend wrapping the problematic functionality in an Apex class that can be mocked with the Stub API. The class and methods to be mocked can not be:

  • static or future methods
  • private methods
  • properties (getters and setters)
  • inner classes
  • classes that only have private constructors

So you would make a public UserManagementWrapper class with it's own public initRegisterVerificationMethod and deregisterVerificationMethod methods that are direct pass throughs to the underlying UserManagement implementations.

Then, in your test methods you can use MockUtil to register your own mock implementation of UserManagementWrapper that returns whatever you need the method being tested to see.

Yes, it is a pain to have to perform these extra steps, but it should get you to a testable setup with minimal impact on the core code.

On the plus side, it will give you more control over what methods like initRegisterVerificationMethod return so you can simulate various return error messages.


A less ideal but maybe easier alternative to mocking is to use Test.isRunningTest() in your core Apex code to alter the code path when testing and avoid the problem methods. However, it does bring more testing mess into the main code flow.

| improve this answer | |
  • 1
    Thanks for getting back to me! I ended up raising a case with SF Premiere support, they're aware of the Stub API, but are doing some internal investigations... I'll let you know if this actually comes up with something :) – Tim Dec 4 '19 at 9:41
  • Salesforce have confirmed that none of these methods can be tested.... soooooo looks like I'll be working with the Stub API D: – Tim Dec 12 '19 at 16:34
  • The Sub API doesn't support the System.UserManagement class I end up getting this error: FATAL_ERROR System.TypeException: Test.createStub() can only be called with classes in the current namespace – Tim Dec 12 '19 at 17:03
  • @Tim That is why I suggested wrapping it in your own class that would meet the stubbing requirements. – Daniel Ballinger Dec 12 '19 at 20:22

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.