5

I'm writing a test for a schedulable class and so far it's working fine but I can't get the return statement to be covered by the test class. I'm missing something here and can't see it.

The final returns on getMonthName (return Name) and getQuarter(Return Quarter) methods are not covered by the test class.

Here the Schedulable Class:

global class MRD_LATAM_LeaseCorrection implements Schedulable {



global void execute(SchedulableContext SC) 
    {
        ListofLeases();
    }


global static void ListOfLeases (){

    List<Margin_Report_Data__c> Corrections = new List<Margin_Report_Data__c>();

//Loop for year

    Integer m=date.today().month();
    for (Integer y=date.today().year(); y <= 2026 ; y++ ){

//Loop for month



        while (m <=12){


            Margin_Report_Data__c mrd = new Margin_Report_Data__c();
            mrd.Account__c='001f000001Fd5sIAAR';
            mrd.Customer__c = 'LATAM AIRLINES GROUP S.A.';
            mrd.Transaction_Date__c = date.newInstance(y, m, 1);
            mrd.Transaction_Year__c = string.valueOf(y);
            mrd.Transaction_Month__c = getMonthName(m);
            mrd.Transaction_Quarter__c = getQuarter(m);
            mrd.Program_Revenue__c = 'No';
            mrd.Sale_Category__c='Leases';
            mrd.Region__c='LA';
            mrd.Total_Sales__c = 194800;
            mrd.Total_Gross_Profit__c = 194800;
            mrd.Post_Status__c = 'Posted';
            mrd.Transaction_Description__c = 'Sale Income Adjustment for LATAM Lease';

            Corrections.add(mrd);
            m++;

        }

        m=1;
    }

    for (Margin_Report_Data__c mr : Corrections){
    }    


insert Corrections;        
}

//Converting month number to month name
global static String getMonthName (Integer Month){
    String Name;
    Switch on Month{
        when 1 {
            Name='January';
          return Name;  
        }
        when 2 {
            Name='February';
          return Name;  
        }
        when 3 {
            Name='March';
          return Name;  
        }
        when 4 {
            Name='April';
          return Name;  
        }
        when 5 {
            Name='May';
          return Name;  
        }
        when 6 {
            Name='June';
          return Name;  
        }
        when 7 {
            Name='July';
          return Name;  
        }
        when 8 {
            Name='August';
          return Name;  
        }
        when 9 {
            Name='September';
          return Name;  
        }
        when 10 {
            Name='October';
          return Name;  
        }
        when 11 {
            Name='November';
          return Name;  
        }
        when 12 {
            Name='December';
          return Name;  
        }
    }
    return Name; //<-- This is not covered
}

//Converting month number to year quarter
global static String getQuarter (Integer Month){
    String Quarter;
    Switch on Month{
        when 1 {
            Quarter = 'Q1';
          return Quarter;  
        }
        when 2 {
            Quarter = 'Q1';
          return Quarter;  
        }
        when 3 {
            Quarter = 'Q1';
          return Quarter;  
        }
        when 4 {
            Quarter = 'Q2';
          return Quarter;  
        }
        when 5 {
            Quarter = 'Q2';
          return Quarter;  
        }
        when 6 {
            Quarter = 'Q2';
          return Quarter;  
        }
        when 7 {
            Quarter = 'Q3';
          return Quarter;  
        }
        when 8 {
            Quarter = 'Q3';
          return Quarter;  
        }
        when 9 {
            Quarter = 'Q3';
          return Quarter;  
        }
        when 10 {
            Quarter = 'Q4';
          return Quarter;  
        }
        when 11 {
            Quarter = 'Q4';
          return Quarter;  
        }
        when 12 {
            Quarter = 'Q4';
          return Quarter;  
        }
    }
    return Quarter; //<-- This is not covered


   }   
}

Here's the test class:

 @IsTest
public class MRD_LATAM_LeaseCorrection_Test {

    @IsTest static void TestDeleteData(){


        //Setup Margin Report
        Margin_Report_Data__c MarginReport = new Margin_Report_Data__c (Transaction_Month__c='January', Transaction_Year__c='2018', Document_Number__c='10', Sales_Order__c='12345');


        Test.startTest();

        MRD_LATAM_LeaseCorrection mrdc = new MRD_LATAM_LeaseCorrection();
        String sch = '0 59 12 * * ?';
        system.schedule('Test MR Lease Correction', sch, mrdc);
        mrdc.execute(null);        

        Test.stoptest();

        List <Margin_Report_Data__c> resultmr = [SELECT id FROM Margin_Report_Data__c WHERE Customer__c = 'LATAM AIRLINES GROUP S.A.' AND Sale_Category__c = 'Leases'];


        System.debug('MRD With Leases?:'+ resultmr.size());
        System.assertEquals(true, resultmr.size()>0);

    }

}

3 Answers 3

7

You have unreachable statements, and your code is overly complicated. You should remove your use of switch statements entirely.

I recommend you simplify the logic for getting quarter to something like the below:

public Integer getQuarterNumber(Integer month)
{
    return 1 + (Integer)Math.floor((month-1)/3);
}
public String getQuarterName(Integer month)
{
    return 'Q' + getQuarterNumber(month);
}

This code should be very easy to cover.


As for getting the name of the month, I would use standard Datetime formatting and keep this logic out of your own code. Something like below would be very easy to use and cover, and should update based on the running user's language:

String getMonthName(Integer month)
{
    return Datetime.newInstance(2000, month, 15, 0, 0, 0).format('MMMM');
}

If you want to guarantee the month name is in English, a more classic approach will give you what you want without forcing you to hit every single execution path in your test. Use a Map.

static final Map<Integer, String> monthNames = new Map<Integer, String>
{
    1 => 'January',
    2 => 'February',
    // etc
};
public static String getMonthName(Integer month)
{
    return monthNames.get(month);
}

One advantage to using a map over a list here is you do not have to worry about bound checking or exceptions based on invalid input. You will simply get a name of null if you pass in 15 as your month number, for example.

5
  • I guess they are not unreachable, but I thought switch statements wouldn't compile without a when else.
    – Adrian Larson
    Commented Mar 22, 2019 at 14:52
  • Got it. Thanks for your explanation. Should I use Switch for more complex logic evaluations then? Commented Mar 22, 2019 at 15:28
  • Also, I need the month names to be always in english in the format of the code because that is being used for other triggers and other logics. Can that be done with your approach? Commented Mar 22, 2019 at 15:30
  • @AlejandroFlores I would only use switch statement when you can't find an alternative. I'll add a way you can guarantee the month names are in English, but if that is the language on all users in your system it may be moot anyway.
    – Adrian Larson
    Commented Mar 22, 2019 at 15:33
  • Can you edit the your answer? I'm trying to correct how to get the values of the mapping in your code but for some reason I can't do it, maybe I don't have enough reputation. Initially it was return MonthNames(); but this was not compiling so I changed it to return monthNames.get(month) and works fine. Thanks again. Commented Mar 22, 2019 at 17:46
4

The golden rule of unit testing is that you only gain coverage for code that is executed as part of a unit test.

Looking at your switch statements in both of your helper methods, if your input matches one of the when criteria, you set a value and then immediately return. Those return lines don't just pop you out of the switch, it pops you out of the entire method.

Thus, if your tests are only providing input that will match one of the when criteria (the "happy path"), you'll never reach those final return lines in your two helper methods.

No reach = no execution = no coverage.

Honestly though, all of those return lines inside of your when blocks are only hurting you here. The behavior of switch in Apex is to execute either one or zero blocks inside the switch, and then move on to the next statement.

If you were to remove the return; line in each of your when blocks, you would reach the final return; at the end of both of your helper methods and you'd get your coverage for that line.

If you do follow that advice, then you should also add some more error checking (what if month is null? what if it's less than 1? what if it's greater than 12?) as well as test methods to stress those situations.

Looking at the other answers though, I agree that switch isn't really the best tool for the job here.

4
  • The reason why I have that final return is because otherwise it wasn't compiling but my understanding was that when a switch block executes the return doesn't pop you out of the entire method but I guess I have to many returns in there. I did this because I saw an example of the Switch and they were doing this but I guess that was a bad practice. Commented Mar 22, 2019 at 15:51
  • @AlejandroFlores Can you share the resource that you used as the basis for your code? You may have misinterpreted something, or maybe the resource you found is low-quality and should be avoided.
    – Derek F
    Commented Mar 22, 2019 at 16:25
  • @AlejandroFlores The reason why Salesforce was complaining was because there were certain paths that could be taken (null integer, or a value outside of [1,12]) where you would not encounter a return statement. If you had added a when else{} block with a return statement, Salesforce would've been just fine with it. That said, I still think that a single return statement (if there weren't a better alternative to using switch outside of the switch is better practice.
    – Derek F
    Commented Mar 22, 2019 at 16:33
  • I lost the source between computers, sorry. I'll owe you that one. Commented Mar 22, 2019 at 17:16
3

You would need to pass in a null value to the method to reach the "default" value. Since that's not possible, you might consider optimizing your code to return a value in default, which should negate the need for a default return.

global static String getMonthName (Integer Month){
    Switch on Month{
        when 1 { return 'January'; }
        when 2 { return 'February'; }
        when 3 { return 'March'; }
        when 4 { return 'April'; }
        when 5 { return 'May'; }
        when 6 { return 'June'; }
        when 7 { return 'July'; }
        when 8 { return 'August'; }
        when 9 { return 'September'; }
        when 10 { return 'October'; }
        when 11 { return 'November'; }
        when else { return 'December'; }
    }
}

Switch statements can do what you're doing, but this would be just as efficient:

static String[] monthNames = new String[] { 'January','February','March','April','May','June','July','August','September','October','November','December' };
global static string getMonthName(Integer month) {
  return monthNames[month-1];
}

You must log in to answer this question.

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