1

The prebuilt class LightningLoginFormController has a try/catch block that I am trying to cover in test.

@AuraEnabled
public static String login(String username, String password, String startUrl) {
    try{
        ApexPages.PageReference lgn = Site.login(username, password, startUrl);
        aura.redirect(lgn);
        return null;
    }
    catch (Exception ex) {
        return ex.getMessage();            
    }
}

I am having trouble getting the login to throw an exception. My current test code is:

     try {
  String newRes = LightningLoginFormController.login(null, null, null);
         system.debug('New Result is ' +newRes);
     } catch (DmlException ex) {
            System.assertEquals('expected text', ex.getMessage());
        }  

 }

But all it does is return null. Any ideas on how I get the Login method to throw an exception?

My new code, based on @Adrian Larson suggestion is now as follows:

Class

global class LightningLoginFormController {

<… other code that is already covered …>

    @AuraEnabled
    public static String login(String username, String password, String startUrl) {
        try{
            ApexPages.PageReference lgn = Site.login(username, password, startUrl);
            aura.redirect(lgn);
            return null;
        }
        catch (Exception ex) {
            return ex.getMessage();            
        }
    }

    
    static LoginService service = new LoginService();
    @TestVisible static void setMock(LoginService mock) { service = mock; }
    public virtual class LoginService
    {
        protected virtual ApexPages.PageReference login(String username, String password, String startUrl)
        {
            return Site.login(username, password, startUrl);
        }
    }
}

Test Class

@IsTest
public without sharing class LightningLoginFormControllerTest {

 <… other code that already works …>
    
    @IsTest static void testLogin()
    {
        LightningLoginFormController.setMock(new MyMock());
        
        Test.startTest();
        LightningLoginFormController.login('testUser', 'fakepwd', null);
        Test.stopTest();
        
    system.assertEquals(null, 'Exception',
        'The exception should be caught');
    }
    class MyMock extends LightningLoginFormController.LoginService
    {
        protected override ApexPages.PageReference login
            (String username, String password, String startUrl)
        {
            throw new DmlException();
        }
     }
}

The problem is the Catch block is still not getting caught and some of the additional code (the public virtual class LoginService method) is not being caught either.

2

1 Answer 1

2

Great example of how dependency injection can help you out with testing. You have a dependency (Site.login) which you want to express greater control over. Rather than work within its constraints, you can sidestep them by injecting a different behavior. The basic syntax would be to add the following lines to your controller.

static LoginService service = new LoginService();
@TestVisible static void setMock(LoginService mock) { service = mock; }
public virtual class LoginService
{
    protected virtual ApexPages.PageReference login(String username, String password, String startUrl)
    {
        return Site.login(username, password, startUrl);
    }
}

Make sure you update your controller to swap to this dependency instead (therefore allowing injection).

ApexPages.PageReference lgn = Site.login(username, password, startUrl); // before
ApexPages.PageReference loginResult = service.login(username, password, startUrl); // after

Then, in your test, you can simply define an extension of this service that instead throws your desired error. Please note that your current assertion structure is deeply flawed, as if you never throw any error you don't even assert anything.

@IsTest static void testLogin()
{
    MyController.setMock(new MyMock());
    
    Test.startTest();
        MyController.login(...);
    Test.stopTest();
    
    system.assertEquals(null, expectedException,
        'The exception should be caught');
}
class MyMock extends MyController.LoginService
{
    protected override ApexPages.PageReference login
        (String username, String password, String startUrl)
    {
        throw new DmlException();
    }
}
7
  • Thanks. @cropredy has been trying to get me to learn more about dependency injections. And I swear I am trying but I have a long way to go. I added what you suggested (or at least my understanding of what you suggested) to my original post. I am still not getting the catch block covered, and now the public virtual class LoginService is not covered either. Did I misinterpret how to implement your suggestion? Mar 29, 2023 at 23:08
  • 1
    Jonathan - your line ApexPages.PageReference lgn = Site.login(username, password, startUrl); needs to be ApexPages.PageReference lgn = service.login(username, password, startUrl);
    – cropredy
    Mar 29, 2023 at 23:40
  • Thanks. That did the trick. Mar 30, 2023 at 0:25
  • Adrian, I updated your answer with the bit Cropredy added. Hope you don't mind. Mar 30, 2023 at 0:30
  • 1
    Adrian, it took me a while but I got there in the end. This is the point of swapping the dependency and using the override. Thanks again for the lesson. Mar 31, 2023 at 20:23

You must log in to answer this question.

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