0

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:

  1. the record returned by the standard controller getRecord() method does not contain a valid OpportunityId, whereas it is correctly set from the oppid passed in the page URL parameters in the real life (as opposed to test) situation.
  2. the standard controller save() method does not create a record and returns a null page reference. Likewise the standard controller getId() 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 a ApexPages.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 **
}
3
  • where is the 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...
    – Derek F
    Commented Sep 8, 2021 at 12:45
  • @Derek: I did not include all the details of the creation of the test data as it would only confuse. The Test method shown is only one in a great many test methods contained in my test class - all of which actually use the getOpportunity(). The root problem, as stated in my post is IMHO the fact that the OpportunityId field of the record returned by StandardController.getRecord() is null and stays null.
    – willeg
    Commented Sep 8, 2021 at 17:38
  • and it's completely expected to be null if you never actually insert an Opportunity in your test (not sure if it's possible to avoid an insert by specifying a record Id in-memory via name=value pairs when you call new Opportunity()). Hence my question about getOpportunity(). 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.
    – Derek F
    Commented Sep 8, 2021 at 17:45

0

You must log in to answer this question.

Browse other questions tagged .