Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

Struggling with how best to test my controller extension - My extension onload sets a picklist value based on the month number. My issue is I can only test current month e.g. Nov. If I explicitly set it to Dec, the extension still only tests Nov. I want to test all the months. Not sure how to setup / structure the test to cover all months. Any help appreciated.

public class CommissionClaimControllerExtension {
    public CommissionClaimControllerExtension(ApexPages.StandardController controller){
        Commission_Claim__c plClaim = (Commission_Claim__c) controller.getRecord();     
        List<Opportunity> opp = [select Type_of_campaign__c,CC_Total_No_of_Leads_Published__c from Opportunity where Id =: plClaim.CC_Opportunity__c limit 1];

        // On Page Load Set Defaults    
        if (opp.size() > 0){     
            plClaim.CC_Campaign_Type__c = opp[0].Type_of_campaign__c;
            plClaim.CC_Total_Claimed__c = opp[0].CC_Total_No_of_Leads_Published__c;
        }

        date dtDate = date.today();
        integer intMonth = dtDate.month();

        System.debug('DEBUG/CA: month number is ' + intMonth); 

        if (intMonth == 1){
            plClaim.CC_Month__c = 'Jan';
        } else if (intMonth == 2){
            plClaim.CC_Month__c = 'Feb';
        } else if (intMonth == 3){
            plClaim.CC_Month__c = 'Mar';
        } else if (intMonth == 4){
            plClaim.CC_Month__c = 'Apr';
        } else if (intMonth == 5){
            plClaim.CC_Month__c = 'May';
        } else if (intMonth == 6){
            plClaim.CC_Month__c = 'Jun';
        } else if (intMonth == 7){
            plClaim.CC_Month__c = 'Jul';
        } else if (intMonth == 8){
            plClaim.CC_Month__c = 'Aug';
        } else if (intMonth == 9){
            plClaim.CC_Month__c = 'Sep';
        } else if (intMonth == 10){
            plClaim.CC_Month__c = 'Oct';
        } else if (intMonth == 11){
            plClaim.CC_Month__c = 'Nov';
        } else {
            plClaim.CC_Month__c = 'Dec';
        }
    }              
}

Here is my test class so far

public class CommissionClaimTests {
    // Inst
    static PageReference ccForm;
    static PageReference ccEditForm;
    static CommissionClaimControllerExtension ext;

    static testMethod void testMyControllerExtension_Nov() {
        ccForm = Page.CommissionClaim;
        test.setCurrentPage(ccForm);

        exclude_profiles__c setting = TestObjects.createCustomSetting();
        Account accts = TestObjects.createTestAccounts();
        Opportunity opps = TestObjects.createTestOpps(accts.Id);

        Commission_Claim__c claim = new Commission_Claim__c();

        ApexPages.Standardcontroller con = new ApexPages.Standardcontroller(claim);
        ext = new CommissionClaimControllerExtension ( con );
        test.startTest();
        claim.CC_no_of_Leads_or_DD_Published__c = 1;
        claim.CC_Month__c = 'Nov';
        system.assert(con != null);
        con.Save();
        test.stopTest();    
    }
}

Thanks

share|improve this question
add comment

2 Answers

My first edit had the Dependency Injection approach @BobBuzzard talks about in his answer, but this wouldn't work without some restructuring of your code, because you're doing stuff in your constructor.

To make DI work,

Changes to Controller : All of the actual logic of setting the Month would need to move to an init() method. In the constructor itself - you'd need to change as follows :

public class CommissionClaimControllerExtension {
    public CommissionClaimControllerExtension(ApexPages.StandardController controller){

if(!Test.isRunningTest()){ // don't init if test, as we will from Test Class
intMonth = Date.today().month(); //BAU - set to the current month
init() ; //move all logic into an init method, so it can be invoked from test
}
}

public integer intMonth {get; set; } //expose as property to access from test

public void init() {
// all the logic moved from the constructor lives here
}

In the Test Class :

ApexPages.Standardcontroller con = new ApexPages.Standardcontroller(claim);
   ext = new CommissionClaimControllerExtension ( con );
   ext.intMonth = '12'; //set to DEC
   ext.init();  //initialize what would normally be done by the constructor
//do your tests

Approach 2 :

Without knowing about the complexity, I would think testing the bit that translates the integer into the month could be tested differently from the setting of the picklist (there may be a dependency which isn't evident in the code)

You are explicitly using today's date in your controller to grab the month :

 date dtDate = date.today();
 integer intMonth = dtDate.month();

Rather than using date.today, this should be set dynamically from the Test Class. It should default to today's date only when a Test is not running (In your controller itself, you can get the MONTH in a more efficient way than using so many if's)

//if a test is running, you have already set the month from the test class
       if(!Test.isRunningTest()){  //get today's date only if not test

        plClaim.CC_Month__c = Datetime.newInstance(2012,intMonth,1).format('MMM'); //Jan, Feb...Dec

        }

In your test class, setting the month directly on the Commission_Claim__c should suffice

claim.CC_Month__c = 'Nov';

You can set this in a loop : (and test in a loop, if you dont need an explicity startTest and stopTest for every month)

String month ;
for (integer i=1; i<=12; i++){
claim.CC_Month__c = Datetime.newInstance(2012,i,1).format('MMM');
}

Special Consideration : Optimizations :

Rather than a web of if's, you can use this to get your Month

Datetime.newInstance(2012,intMonth,1).format('MMM'); //Jan, Feb...Dec
share|improve this answer
    
This makes sense, this is close to path I took. –  Michael Gill Nov 4 '12 at 11:57
    
Have a look at the Edit. –  techtrekker Nov 4 '12 at 13:02
    
This works well & is the least amount of coding. Only issue is I can't get coverage on this part if (!Test.isRunningTest()){ 21 intMonth = Date.Today().month(); 22 init(); 23 } –  Michael Gill Nov 4 '12 at 22:54
    
Yeah, that one line will not execute when its run as part of a Test, so a little less than 100%. If you get rid of the condition, it runs as part of test, so the same code runs twice, once via constructor, second via test class.(Hopefully you also noticed the optimisation I suggested - lesser script statements to get month name. Hope it helps.) –  techtrekker Nov 4 '12 at 22:58
    
sure did. Going to bed :-) –  Michael Gill Nov 4 '12 at 23:22
add comment

In this type of situation I tend to use dependency injection to set the date from the test method. This would require you to move the pcClaim initialisation out to a getter method (thanks tech trekker for spotting this). The date is initialised in the constructor to the current date:

private Date dt;
public MyController()
{
   dt=System.today();
}

public Date injectDate(Date inDt)
{
   dt=inDt;
} 

Then in my test class I can have:

MyController ctrl=new MyController();
ctrl.injectDate(Date.newInstance(2012, 12, 10);

this is also a candidate for placing the test methods in the same class as the controller, as that would allow you to make the injectDate method private, to avoid it being inadvertently used elsewhere.

In your case its a little less clear cut, as you are setting up the value in the constructor. Thus I'd move that section of code out to a helper method.

You'd need to move the plClaim out as a controller property:

private Commission_Claim__c plClaim;

The constructor would then contain:

    date dtDate = date.today();
    setupMonthPicklist(dtDate);

and the helper method:

 public void setupMonthPickList(Date theDate)
 {
    integer intMonth = dtDate.month();

    System.debug('DEBUG/CA: month number is ' + intMonth); 

    if (intMonth == 1){
        plClaim.CC_Month__c = 'Jan';
    } else if (intMonth == 2){
        plClaim.CC_Month__c = 'Feb';
    } else if (intMonth == 3){
        plClaim.CC_Month__c = 'Mar';
    } else if (intMonth == 4){
        plClaim.CC_Month__c = 'Apr';
    } else if (intMonth == 5){
        plClaim.CC_Month__c = 'May';
    } else if (intMonth == 6){
        plClaim.CC_Month__c = 'Jun';
    } else if (intMonth == 7){
        plClaim.CC_Month__c = 'Jul';
    } else if (intMonth == 8){
        plClaim.CC_Month__c = 'Aug';
    } else if (intMonth == 9){
        plClaim.CC_Month__c = 'Sep';
    } else if (intMonth == 10){
        plClaim.CC_Month__c = 'Oct';
    } else if (intMonth == 11){
        plClaim.CC_Month__c = 'Nov';
    } else {
        plClaim.CC_Month__c = 'Dec';
    }
}              

Then your test method, after instantiating the extension controller, can then execute the setupMonthPickList method with a different date, to override the value:

ext = new CommissionClaimControllerExtension ( con );
ext.setupMonthPickList(Date.newInstance(2012, 12, 1);

To my mind, the advantage of this approach over using something like the Test.isRunningTest() mechanism, is that the same code is being executed when running live and in the test context - its just in the test context the date is being supplied from outside the constructor. In your situation there's not a lot to choose between the two approaches, but from a best practice point of view I always try to avoid having test code that is different to production code, as it feels like I'm writing code just to get tests to pass rather than actually testing what the code should do.

share|improve this answer
2  
Dependency Injection was my first thought, however isn't that a candidate only when the processing is happening in an instance method rather than in the constructor. Here the picklist will already have initialised in the constructor. Setting the date after the constructor has executed wont achieve much? Maybe I'm missing something. –  techtrekker Nov 4 '12 at 9:10
    
Good point, I was assuming that the pick list was outside the constructor - I've updated the answer. DI is better in my opinion, as otherwise you aren't testing the code that runs in production, rather you are changing the code to satisfy the test. –  Bob Buzzard Nov 4 '12 at 9:30
    
Not sure I understand this fully, but it seems a better way. MyController is my custom extension? Is the injectDate method correct. I have tried adding this into my custom extension, but it appears to be missing something. Should it be public void Date injectDate(Date inDt) or return something.. Sorry if this is a stupid question –  Michael Gill Nov 4 '12 at 12:02
    
I've revised my answer as it was just presenting a generic DI point of view rather than your specific situation. Hopefully makes a bit more sense now. –  Bob Buzzard Nov 4 '12 at 14:05
    
+1 on DI, however my only reason for adding the .isRunningTest check in the constructor, is that its a wasted call in the context of a Test class, where you are going to invoke the initializing method from the Test Class anyway. So any values set via the constructor invocation are in vain and will get overwritten. –  techtrekker Nov 4 '12 at 14:12
show 2 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.