0

I am trying to successfully run Site.forgotPassword() in a test class, but the method is returning False when used in my test class. I have tried using real Users (as demonstrated in the code below), and creating a User as part of the test. Without having the Site.forgotPassword() run successfully, the if(success) code block below does not receive code coverage, which is needed for business purposes.

Does anyone know why Site.forgotPassword() would not function within a test environment?

Forgot password controller:

public with sharing class ForgotPasswordController {

    public String username {get; set;}   

    public ForgotPasswordController() {}

    public PageReference forgotPassword() {

        boolean success = Site.forgotPassword(username);
        PageReference pr = Page.ForgotPasswordConfirm;
        pr.setRedirect(true);

        if (success) {              
            return pr;
        }
        return null;
    }
}

Test Class:

@IsTest(SeeAllData=true) 
public static void testForgotPasswordControllerSuccess() {

    ForgotPasswordController controller = new ForgotPasswordController();

    User testUser = [SELECT Id, UserName FROM User WHERE IsActive = True LIMIT 1];

    controller.username = testUser.UserName;

    PageReference forgotPasswordResult;

    system.runAs(testUser){
        forgotPasswordResult = controller.forgotPassword();
    }

    System.assertEquals(forgotPasswordResult,null); 
}
  • Your assert statement should be (expected val, actual val, msg) System.assertEquals(false,forgotPasswordResult,'The return value is always true unless it’s called outside of a Visualforce page'); – cropredy Jan 11 at 1:24
  • @cropredy The issue I am facing is that my test code is not reaching the if(success) code block, and thus that line is not receiving code coverage. I need the Site.forgotPassword() code to return True. (I've updated my post for clarity) – Jwok Jan 13 at 17:46
  • well, if code coverage is your issue replace the last 3 lines w/ return success ? pr : null; Otherwise, you need a mocking framework (which is how I would deal with this) – cropredy Jan 13 at 18:05
  • @cropredy That's a clever workaround. For completion's sake, how would one create a mocking framework? – Jwok Jan 13 at 18:49
0

Your testmethod does:

PageReference forgotPasswordResult;

system.runAs(testUser){
    forgotPasswordResult = controller.forgotPassword();
}

System.assertEquals(forgotPasswordResult,True);

But the controller method forgotPassword returns PageReference, not a Boolean

So, the last assert should be:

System.assertEquals(Page.ForgotPasswordConfirm,forgotPasswordResult,
     'forgotten user pw should return pgRef to Forgot PW page');

If, for some reason, in a testmethod context, Site.forgotPassword(..) never returns true, then you need a way to mock the Site.forgotPassword(..) method

This is beyond the scope of the answer but there are three basic approaches:

  • Use Test.isRunningTest to coerce a response. This requires adding condition logic used only for testing in your main code (yuck factor = high)

  • Create an Interface ISitesService with method forgotPassword. Create a new class called SitesService that implements ISitesService. This class will do the actual calls to Site.xxx. Have the controller use an object of type ISitesService and then invoke myInterfaceObj.forgotPassword(..). Then using dependency injection, you tell the controller whether to use the PROD version of myInterfaceObj or a testmethod-definned inner class that implements ISitesService. This approach is very similar to the HttpCalloutMock approach

  • Same as above but instead of the testmethod inner class that implements ISitesService you use ApexMocks package on GitHub that allows you to inject your own responses to each method defined by ISitesService

|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.