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
Attribution to: Michael Gill
Possible Suggestion/Solution #1
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
Attribution to: techtrekker
Possible Suggestion/Solution #2
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.
Attribution to: Bob Buzzard
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/4017