Updated Sep 10, 2021; see at end.
I have written a VF New Quote page that uses a controller extension. The purpose of the extension is to initialize certain custom fields in the Quote and the QuoteLineItems with values from the corresponding Opportunity and OpportunityLineItems.
The Extension works as intended. The problem I have is in creating my test method:
- the record returned by the standard controller
getRecord()
method does not contain a valid OpportunityId, whereas it is correctly set from theoppid
passed in the page URL parameters in the real life (as opposed to test) situation. - the standard controller
save()
method does not create a record and returns anull
page reference. Likewise the standard controllergetId()
method never returns a value.
So, how do I get my test method to work, and get sufficient test coverage?
Test Method
@isTest
static void TestControllerExtension() {
Opportunity opp = getOpportunity();
System.assert(opp.Id != null);
// set Q2ONewQuote page reference
System.PageReference ref = Page.Q2ONewQuote;
System.Test.setCurrentPage(ref);
// set parameters - as shown in Salesforce UI
ApexPages.currentPage().getParameters().put('retUrl', EncodingUtil.urlEncode('/' + (String)opp.Id, 'UTF-8'));
ApexPages.currentPage().getParameters().put('oppid', (String)opp.Id);
ApexPages.currentPage().getParameters().put('sfdc.override', '1');
System.debug(ref);
// instantiate the page
ApexPages.StandardController sc = new ApexPages.StandardController(new Quote());
Q2ONewQuoteControllerExt controllerExt = new Q2ONewQuoteControllerExt(sc);
Quote quote = (Quote)sc.getRecord();
quote.OpportunityId = opp.Id; // NEEDED for getQuote() to succeed
System.assert(quote.OpportunityId != null, 'unexpected: quote.OpportunityId is null.');
quote = controllerExt.getQuote();
// call save()
ref = controllerExt.save();
System.debug(ref);
// test result
System.Assert(sc.getRecord() != null, 'unexpected: sc.getRecord() returns null.');
System.Assert(controllerExt.getQuote() != null, 'unexpected: controllerExt.getQuote() returns null.');
Id newId = sc.getId();
System.assert(newId != null, 'Unexpected: sc.getId() is null.');
// test Quote creation
Quote newQuote = [SELECT Id, OpportunityId FROM Quote WHERE OpportunityId = :opp.Id LIMIT 1];
System.assert(newQuote != null, 'Unexpected: new Quote does not exist.');
}
Controller Code (Excerpt)
/* ==================================================
*
* Public Controller Interface
*
* ==================================================
*/
private final ApexPages.StandardController stdController;
private final Quote quote;
public Q2ONewQuoteControllerExt(ApexPages.StandardController stdController) {
this.stdController = stdController; // keep reference to stdController
this.quote = (Quote)stdController.getRecord();
}
// ------------------------------------------------
// getQuote
// ------------------------------------------------
private Boolean isInitialized = false;
public Quote getQuote() {
System.assert(quote.OpportunityId != null, 'unexpected: quote.OpportunityId is null.');
// only in create case (Id == null) and only once
if (!isInitialized && stdController.getId() == null) {
getOpportunity(); // get opportunity
getAccount(); // get account
getQuoteLineItems(); // create quote lines, copying standard and custom fields from opportunity lines
initQuoteFields(); // copy custom synced fields from Opportunity, adress fields from Opportunity.Account
}
isInitialized = true;
return quote;
}
// ------------------------------------------------
// save
// ------------------------------------------------
public System.PageReference save() {
// call standard controller to save the quote and give us the page reference
PageReference redir = stdController.save();
if (redir == null) return null; // error occurred: stay on the same page
// copy line items
try {
// patch the line items' QuoteId
setQuoteIdsInLineItems();
// insert line items
database.insert(getQuoteLineItems());
} catch (System.Exception e) {
System.ApexPages.addMessages(e);
System.ApexPages.addMessage(new ApexPages.message(ApexPages.severity.Error, 'No Line Items have been copied to the new Quote.'));
return null; // error occurred: stay on the same page
}
// redirect to where standard controller tells us
return redir;
}
Update
(after some experimentation: thanks also to @Derek)
You find the working test class code below.
- most examples contain a
System.Test.setCurrentPage(ref)
and at least aApexPages.currentPage().getParameters().put('id', (String)someId)
lines. In my case, it turned out that none of this is actually necessary.
@isTest
static void TestControllerExtension() {
// instantiate controller ad extension
ApexPages.StandardController stdController =
new ApexPages.StandardController(new Quote(OpportunityId = getOpportunity().id));
Q2ONewQuoteControllerExt controllerExt =
new Q2ONewQuoteControllerExt(stdController);
// call getQuote in order to execute all initialization tasks (the page is never instantiated!)
Quote quote = controllerExt.getQuote();
// set the required quote number field (there is no user to do this)
quote.Name = '001';
// call the extension's save();
System.Pagereference ref = controllerExt.save();
// test Quote creation
List<Quote> quotes =
[SELECT Id, OpportunityId FROM Quote WHERE OpportunityId = :getOpportunity().Id];
System.assertEquals(1, quotes.size(), 'Unexpected - new Quote does not exist');
// ** more asserts to test the values in the quote and the quote line items **
}
getOpportunity()
method defined, and what does it do? In unit tests, you're responsible for creating (and inserting) any records that your SUT (system under test) needs such as Accounts, Opportunities, Products, PricebookEntries, etc...getOpportunity()
. The root problem, as stated in my post is IMHO the fact that the OpportunityId field of the record returned byStandardController.getRecord()
is null and stays null.new Opportunity()
). Hence my question aboutgetOpportunity()
. You've gotta provide enough relevant context for other people to be able to understand your issue if you expect to get a decent answer.