You need to sign in to do that
Don't have an account?
SIB Admin
Trouble with Apex Test Class
I've just finished re-factoring some an old apex controller I wrote a while back. Field testing in the sandbox as far as functionality has shown it works great. Now I'm trying to write the test class for it and I'm having some serious issues. I'm unable to call any of the local variables from within the class. The test class stops running once it reaches line the creation of the aWrappers. It's like it doesn't query for any of the opportunities that are related to the audits. Any help would be greatly appreciated.
Apex Controller
Visualforce Page
Apex Controller
/** * Author: Andrew Bettke * Date: 12/8/14 * Last Revision: 10/21/2015 * * The controller handles the 'SIBInvoiceWizard' custom visualforce page. This page will be used by the accounting department to mass create SIB Invoices * (along with billing event records) quickly and seamlessly from one page. The page will be accessed from a button on any active client account detail * page. From there, the page will dynamically displays editable data tables for each savings item that can billed for that month. After submitting the * form with, the controller will analyze the data and created invoice and billing event records accordingly. ** */ public class SIBInvoiceWizard { //The account for which we will be generating an invoice. public Account account {get; set;} //All audits processing savings for this account. public List<auditWrapper> aWrappers {get; set;} public SIB_Invoice__c invoice {get; set;} public Decimal invoiceTotal {get; set;} public Decimal inputTotal {get; set;} private static set<String> variableSavingsSources = new set<String> {'Generic VoIP Proposal','Plan Optimization', 'Rate Reduction','Service Standardization', 'Vendor Change','Vendor Consolidation'}; //Upon creation, initialize variables and fill lists. public SIBInvoiceWizard(){ //Instantiate the account by querying the database for the specified ID account = [SELECT ID, Name FROM Account Where Id = :ApexPages.currentPage().getParameters().get('accId')]; //Instantiate the Invoice and link it to the Account. invoice = new SIB_Invoice__c(); invoice.Account__c = account.Id; invoice.Amount_Paid__c = 0; invoiceTotal = 0.00; inputTotal = 0.00; //Grab a list of all relevant audits and their savings to fill the savings and audit wrappers. List<Location_Audits__c> auditsWithSavings = [SELECT Id, Name, Stage__c, Audit_Type__c, (SELECT ID, Name, Savings_Source__c, Type, Amount, Monthly_Contract_Value__c, CV__c, CV_Total_Billed__c, CV_Remaining__c, of_Months_Remaining__c, Recurrence_Type__c, Account.Contingency_Fee__c //Sub-query FROM Opportunities__r WHERE (StageName = 'Finalized Savings' OR StageName = 'Engagement Complete') AND CV_Remaining__c > 0) FROM Location_Audits__c WHERE Account__r.Id = :account.Id]; //Create a new list of auditWrappers and fill those auditWrappers with audits from the list above and a list of savingsWrappers. aWrappers = new List<auditWrapper>(); for(Location_Audits__c audit : auditsWithSavings){ List<savingsWrapper> sWrappers = new List<savingsWrapper>(); for(Opportunity savingsItem : audit.Opportunities__r){ Savings_Item_Billing_Event__c newSIBE = new Savings_Item_Billing_Event__c(); newSIBE.Savings_Item__c = savingsItem.Id; newSIBE.CV_Billed__c = 0; sWrappers.add(new savingsWrapper(savingsItem,newSIBE)); } if(sWrappers.size() > 0){ aWrappers.add(new auditWrapper(audit,sWrappers)); } } } public void calculateBillingAllocations(){ for(auditWrapper a : aWrappers){ Decimal adjustedCategoryBilling = a.categoryBillingTotal; Decimal includedVariableTotal = 0.00; //First Loop: Assign fixed billing amounts for one-time savings and fixed monthly savings. //While looping, sum the amounts for all included variable savings. for(savingsWrapper s : a.savingsWrappers){ if(s.included == true){ //We have a one-time or fixed montly savings item. if(!variableSavingsSources.contains(s.savingsItem.Savings_Source__c) ){ s.allocationPercent = 100.00; s.sibe.CV_Billed__c = (s.savingsItem.Amount * (s.savingsItem.Account.Contingency_Fee__c/100)).setScale(2,RoundingMode.HALF_UP); adjustedCategoryBilling = adjustedCategoryBilling - s.sibe.CV_Billed__c; } else{ includedVariableTotal = includedVariableTotal + s.savingsItem.Amount; } } else{ s.allocationPercent = 0.00; s.sibe.CV_Billed__c = 0.00; } } //Second Loop: Assign allocated billing amounts for variable monthly savings. for(savingsWrapper s : a.savingsWrappers){ if(s.included == true && includedVariableTotal > 0){ //We have a variable monthly savings item if( variableSavingsSources.contains(s.savingsItem.Savings_Source__c) ){ //Calculate the allocation % for the remaining amount of billing left to allocate. s.allocationPercent = ((s.savingsItem.Amount/includedVariableTotal) * 100).setScale(2,RoundingMode.HALF_UP); s.sibe.CV_Billed__c = (adjustedCategoryBilling * (s.allocationPercent/100)).setScale(2,RoundingMode.HALF_UP); } } } } invoiceTotal = 0.00; inputTotal = 0.00; for(auditWrapper a : aWrappers){ inputTotal = inputTotal + a.categoryBillingTotal; for(savingsWrapper s : a.savingsWrappers){ invoiceTotal = (invoiceTotal + s.sibe.CV_Billed__c).setScale(2,RoundingMode.HALF_UP); } } if(invoiceTotal != inputTotal){ ApexPages.addMessage( new ApexPages.Message(ApexPages.Severity.ERROR,'The amount recieved from data entry does not match the calculated invoice total. Review your input and recalculate.') ); } else{ ApexPages.addMessage( new ApexPages.Message(ApexPages.Severity.CONFIRM,'The input total and calculated invoice total match! You may proceed with submission.') ); } } //Called when the form is submitted. public PageReference formSubmission(){ //Insert the created invoice into the database try{ insert invoice; //Link each billingEvent created back to the newly inserted invoice, then insert the billing events. Integer lineItem = 1; for(auditWrapper a : aWrappers){ for(savingsWrapper s : a.savingsWrappers){ s.sibe.SIB_Invoice__c = invoice.Id; if(lineItem < 10){ s.sibe.Name = invoice.Name + ' - 0' + lineItem; } else{ s.sibe.Name = invoice.Name + ' - ' + lineItem; } if(s.sibe.CV_Billed__c != null || s.sibe.CV_Billed__c == 0){ insert s.sibe; lineItem = lineItem + 1; } } } //Create a new page reference for the invoice and redirect the user to the newly created invoice. PageReference invoiceDetail = new PageReference('/'+invoice.Id); invoiceDetail.setRedirect(true); return invoiceDetail; } catch(DmlException e){ if( e.getMessage().contains('DUPLICATE_VALUE') ){ ApexPages.addMessage( new ApexPages.Message(ApexPages.Severity.ERROR,'Duplicate Invoice ID found. This Invoice # already exists.') ); return ApexPages.currentPage(); } else{ ApexPages.addMessage( new ApexPages.Message(ApexPages.Severity.ERROR,'A generic DML exception has occured. Please contact the system administrator.') ); return ApexPages.currentPage(); } } catch(Exception e){ ApexPages.addMessage( new ApexPages.Message(ApexPages.Severity.ERROR,'A generic exception error has occured. Please contact the system administrator.') ); return ApexPages.currentPage(); } } class auditWrapper{ public Location_Audits__c audit {get; set;} public List<savingsWrapper> savingsWrappers {get; set;} public Decimal recurringVariableTotal {get; set;} public Decimal categoryTotal {get; set;} public Decimal categoryBillingTotal {get; set;} public auditWrapper(Location_Audits__c audit, List<savingsWrapper> savingsWrappers){ this.audit = audit; this.savingsWrappers = savingsWrappers; this.categoryTotal = 0; this.categoryBillingTotal = 0.00; } } class savingsWrapper{ public Opportunity savingsItem {get; set;} public Savings_Item_Billing_Event__c sibe {get; set;} public Decimal allocationPercent {get; set;} public Boolean included {get; set;} public savingsWrapper(Opportunity savingsItem, Savings_Item_Billing_Event__c sibe){ this.savingsItem = savingsItem; this.sibe = sibe; } } }
Visualforce Page
<apex:page controller="SIBInvoiceWizard" docType="html-5.0"> <apex:includeScript value="/soap/ajax/29.0/connection.js"/> <apex:includeScript value="/soap/ajax/29.0/apex.js"/> <style type="text/css"> .col-md { width:10%; } .col-sm { width:5%; } .totals td{ font-weight:bold; } .totals .col1{ width:77%; } </style> <apex:pageMessages /> <apex:sectionHeader title="SIB Invoice Wizard"/> <apex:form id="wizardForm"> <apex:PageBlock id="invoiceDetails" title="Invoice Details"> <table style="table-layout:fixed;width:50%;"> <tr> <td> <apex:outputLabel value="Invoice # " style="font-weight:bold"/> </td> <td> <apex:outputLabel value="Invoice Date " style="font-weight:bold"/> </td> <td> <apex:outputLabel value="Amount Paid " style="font-weight:bold"/> </td> </tr> <tr> <td> <apex:inputField id="invoiceNumber" value="{!invoice.Name}" required="true"/> </td> <td> <apex:inputField id="invoiceDate" value="{!invoice.Invoice_Date__c}"/> </td> <td> <apex:inputField id="amountPaid" value="{!invoice.Amount_Paid__c}"/> </td> </tr> </table> </apex:PageBlock> <apex:PageBlock id="savingsForm" onkeyup="totalBillingEvents();"> <apex:repeat value="{!aWrappers}" var="a"> <apex:pageBlockSection title="{!a.audit.Name + ' (' + a.audit.Audit_Type__c + ')'}" columns="1"> <apex:outputPanel layout="block" style="width:100%;" id="savingsTables"> <table> <tr> <th class="col-md">Name</th> <th class="col-md">Savings Source</th> <th class="col-sm">Type</th> <th class="col-sm">Recurrence Type</th> <th class="col-sm">Allocation Factor</th> <th class="col-sm">Monthly Savings</th> <th class="col-sm">Monthly CV</th> <th class="col-sm">CV Billed</th> <th class="col-sm">CV Remaining</th> <th class="col-sm">Current Billing</th> <th class="col-sm"># of Months Included</th> <th class="col-sm">Item Included</th> </tr> <apex:repeat value="{!a.savingsWrappers}" var="s"> <tr> <td class="col-md"><apex:outputLink value="/{!s.savingsItem.Id}">{!s.savingsItem.Name}</apex:outputLink></td> <td class="col-md">{!s.savingsItem.Savings_Source__c}</td> <td class="col-sm">{!s.savingsItem.Type}</td> <td class="col-sm">{!s.savingsItem.Recurrence_Type__c}</td> <td class="col-sm">{!s.allocationPercent}%</td> <td class="col-sm">${!s.savingsItem.Amount}</td> <td class="col-sm">${!s.savingsItem.Monthly_Contract_Value__c}</td> <td class="col-sm">${!s.savingsItem.CV_Total_Billed__c}</td> <td class="col-sm">${!s.savingsItem.CV_Remaining__c}</td> <td class="col-sm"> <apex:outputText value="{0, number, currency}" style="width:70%;"> <apex:param value="{!s.sibe.CV_Billed__c}"/> </apex:outputText> </td> <td class="col-sm"><apex:inputField value="{!s.sibe.of_Months_Included__c}" style="width:25%;"/></td> <td class="col-sm"><apex:inputCheckbox value="{!s.included}" style="text-align:center;"/></td> </tr> </apex:repeat> <tr class="totals"> <td colspan="9">Total Billed for Audit Category</td> <td><apex:inputText value="{!a.categoryBillingTotal}" style="width:70%;"/></td> </tr> </table> </apex:outputPanel> </apex:pageBlockSection> </apex:repeat> <apex:pageBlockSection columns="1"> <apex:outputPanel layout="block" style="width:100%;"> <table class="totals" style="width:100%;margin-top:2%"> <tr> <td class="col1">Invoice Total</td> <td><apex:outputText value="{0, number, currency}" style="color:{!IF(invoiceTotal==inputTotal,'green','red')};"> <apex:param value="{!invoiceTotal}"/> </apex:outputText> </td> </tr> <tr class="totals"> <td class="col1">Billing Input Total</td> <td><apex:outputText value="{0, number, currency}"> <apex:param value="{!inputTotal}"/> </apex:outputText> </td> </tr> <tr> <td class="col1"></td> <td><apex:commandButton value="Calculate" rerender="" action="{!calculateBillingAllocations}"/></td> </tr> <tr> <td class="col1"></td> <td><apex:commandButton value="Submit" rerender="" action="{!formSubmission}" disabled="{!(invoiceTotal=0 || inputTotal=0 || invoiceTotal != inputTotal) }"/> </td> </tr> </table> </apex:outputPanel> </apex:pageBlockSection> </apex:PageBlock> </apex:form> <script type="text/javascript"> function totalBillingEvents(){ var itemAmountElements = document.getElementById("{!$Component.wizardForm.savingsForm}").getElementsByTagName("input"); var total = 0; for(i = 0; i < itemAmountElements.length; i++){ var currency = itemAmountElements[i].value; var number = Number(currency.replace(/[^0-9\.-]+/g,"")); total = total + number; i = i + 1 } document.getElementById("{!$Component.wizardForm.savingsForm.invoiceTotal}").innerHTML = "$"+total.toFixed(2); } </script> </apex:page>Apex Test Class
/** * */ @isTest private class SIBInvoiceWizardTest { static testMethod void SIBInvoiceWizardMainTest() { Account testAccount = new Account(Name = 'Test Account', RecordTypeId = '012C0000000GCJv', Industry = 'Banking',Status__c = 'Active'); insert testAccount; Location_Audits__c testAudit = new Location_Audits__c(Account__c = testAccount.Id, Stage__c = 'Audit Complete - Processing Savings', Audit_Type__c = 'CO2', Current_Analyst__c = 'Andrew Bettke'); insert testAudit; Opportunity testSavingsItem = new Opportunity(Name = 'testSavings', Account = testAccount, Audit_Number__c = testAudit.Id, of_months__c = 1, StageName = 'Not Yet Proposed', CloseDate = system.today(), Implementer__c = 'Andrew Bettke', Double_Checker__c = 'Andrew Bettke', Expected_Completion_Initial_Validation__c = system.today()); insert testSavingsItem; PageReference testPage = new PageReference('/apex/SIBInvoiceWizard?accId='+testAccount.Id); Test.setCurrentPage(testPage); Test.startTest(); SIBInvoiceWizard wizardController = new SIBInvoiceWizard(); wizardController.invoice.Name = '123456'; wizardController.invoice.Invoice_Date__c = system.today(); //wizardController.aWrappers[0].savingsWrappers[0].sibe.CV_Billed__c = 100; //wizardController.aWrappers[0].savingsWrappers[0].sibe.of_Months_Included__c = 1; //wizardController.aWrappers[0].savingsWrappers[0].included = true; //wizardController.calculateBillingAllocations(); wizardController.formSubmission(); Test.stopTest(); } }
I tried to verify if this was the case, and whenever I tried to System.Assert(auditsWithSavings.size() > 0) the code would not save saying that that variable did not exist. I'm not sure if the test records I'm creating aren't actually being inserted or if the query itself is not looking for the correct records to pull. Does that help at all?
And then if you update your test to this it should work. On line 33 I'm using the Page directly instead of relying on the parsing of the url. This will make things a little more tightly bound so that you don't accidentally break your tests later down the line by removing a page the test is using. And then by adding the parameters to the map (line 34), we everything should be populated correctly.
NOTE: This code has not been tested and may contain typographical / logical errors.