0

I have an apex class that steps a user through a visualforce page. They select a product family and then are presented with a list of products. They can then select one or more of these, via a checkbox, and then submit the page to get the selected items saved into a table. I am trying to write the unit test for this process and am stuck at the checkbox select step. I have created a wrapper class for the list of products, but not sure how to simulate the user selecting a product so that it can be saved, and then checked, for the unit test. I have tried searching but what I find is more about presenting check boxes pre-selected, which is not what I need.

The full visualforce page.

<apex:page Controller="QueryProduct_V2"> 
    
    <apex:pageBlock >
        <apex:pageBlockSection title="Active Quote Detail">
            <apex:pageBlockTable value="{! quotedetails }" var="qd">
                <apex:column value="{! qd.Name }"/>
                <apex:column value="{! qd.Account__r.Name }"/>
                <apex:column value="{! qd.Opportunity__r.Name }"/>
                <apex:column value="{! qd.CurrencyIsocode }"/>
                <apex:column value="{! qd.Id }"/>
            </apex:pageBlockTable>
        </apex:pageBlockSection>
        
        <apex:pageMessages id="showmsg"></apex:pageMessages>
        
        <apex:pageBlockSection title="Product Family selection">
            <apex:form >
                <apex:outputLabel value="Which Family? :"/>
                <apex:selectList value="{! selected_Pricebook}" size="1">
                    <apex:selectOptions value="{! family_name}"/>
                </apex:selectList>
                <br /><br />
                <apex:commandButton value="Show Products" action="{!getProducts}" rerender="out"/>
                <apex:commandButton value="Clear" action="{!clear}"/> 
            </apex:form>
        </apex:pageBlockSection>

        

        
    </apex:pageBlock>
    
    <apex:outputPanel id="out">
        <apex:pageBlock title="Selected Family" rendered="{!If(selected_Pricebook !=null,true,false)}">
            <apex:pageBlockSection >
                <apex:outputText value="{!selected_Pricebook}"/>
            </apex:pageBlockSection>
        </apex:pageBlock>
        
        <apex:pageBlock title="Product Selection" rendered="{!If(selected_Pricebook !=null,true,false)}">
            <br />
            
            <apex:form > 
                Product Filter: 
                <apex:inputText id="filterValue" value="{!filterValue}" />
                <apex:commandButton value="Filter" action="{!getProducts}" rerender="out"/>
                Enter characters to filter product selection.
                <br /><br /><br />
            </apex:form>
            
            
            <apex:form > 
                <apex:pageBlock >
                    <apex:pageBlockButtons >
                        <apex:commandButton value="Add Selected" action="{!submit}" rerender="table"/>
                    </apex:pageBlockButtons>
                    <apex:pageBlockTable value="{!PBEs}" var="wrapper" id="tablesorter" styleclass="tablesorter">
                        <apex:column >
                            <apex:inputCheckbox value="{!wrapper.selected}"/>
                        </apex:column>
                        <apex:column value="{!wrapper.PBE.Name}" />
                        <apex:inputHidden value="{!wrapper.PBE.Product2ID}"/>
                        <apex:column value="{!wrapper.PBE.Product2.ProductCode}" />
                        <apex:column value="{!wrapper.PBE.UnitPrice}" />
                        <apex:column value="{!wrapper.PBE.Product2.Description}" />
                    </apex:pageBlockTable>
                </apex:pageBlock>
            </apex:form>
            <apex:outputPanel rendered="{! ProdError }">
                <h1><div align="center">
                    There are no products that match the currency of this quote in this product family?
                    </div></h1>
            </apex:outputPanel>
            
            
            <apex:outputPanel rendered="{!If(selected_Product !=null,true,false)}">
                <p>You have selected:</p>
                <apex:outputText value="{!selected_Product}"/>
            </apex:outputPanel>   
            
        </apex:pageBlock>
        
    </apex:outputPanel>

</apex:page>

The apex code for the process

public class QueryProduct_V2 {
    
    //Variables for working with the quote
    public string quoteId;
    public static List<Quote__c> myQuote;
    //Variables for working with the pricebook
    public String selected_Pricebook {get;set;}
    public List<String> tmpString;// {get;set;}
    public List<SelectOption> family_name {get;set;}
    public boolean ProdError {get;set;}
    public boolean noBookError {get;set;}
    //Variables for working with the selected product
    public string selected_Product {get;set;}
    public string filterValue {get;set;}
    public boolean errorFlag {get;set;}   
    
    //Used to collect Quote information for this process
    public static List<Quote__c> getQuotedetails() {
        string quoteId =  ApexPages.currentPage().getparameters().get('Id');
        System.debug( 'Quote Id is ' + quoteId);
        myQuote = [SELECT Id, Name, Account__r.Name, Opportunity__r.Name, Opportunity__r.Pricebook2.Id , CurrencyIsoCode, Subscription_Length_Years__c FROM Quote__c where Id =:quoteId];
        system.debug('myQuote' + string.valueOf(myQuote));
        for (Quote__c mq : myQuote) {
            if(string.valueOf(mq.Opportunity__r.Pricebook2) == '' || string.valueOf(mq.Opportunity__r.Pricebook2) == null) {
                ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,'Pricebook not set at the Opportunity level for this quote.  Please Correct, else next step will cause an error.'));
            }
        }
        return myQuote;
    }
    
    public class ProductWrapper {
        public PricebookEntry PBE {get;set;}
        public Boolean selected {get;set;}
        
        public ProductWrapper() {
            this.selected = false;
        }
        
        public ProductWrapper(PricebookEntry PBE) {
            this();
            this.PBE = PBE;
        }
    }
    
    public List<ProductWrapper> PBEs {get;set;}
    
    public void getProducts() {
        //Pricebook for this operation is pulled from the Pricebook setting at the Opp level.
        //Base query string for all searches
        String baseQuery = 'Select Name,Product2Id,Product2.ProductCode,Product2.Family,Product2.BillingType__c,Product2.Description,UnitPrice from PriceBookEntry where Pricebook2Id=\''+myQuote[0].Opportunity__r.Pricebook2.ID+'\' and CurrencyIsoCode =\''+myQuote[0].CurrencyIsoCode+'\' and Product2.Family = \''+selected_Pricebook+'\' and IsActive=TRUE ORDER BY Product2.ProductCode';
        //String modifier for any filtering
        if( !string.ISBLANK(filterValue) ) {
            string searchValue = '\'%'+ filterValue +'%\'';
            baseQuery = baseQuery + ' and Name like '+searchValue;
        }
        System.debug( 'in getProducts, product list query is: ' + baseQuery);
        PBEs = new List<ProductWrapper>();
        
        for (PricebookEntry PBE : database.query(baseQuery)){
            PBEs.add(new ProductWrapper(PBE));
        }
    }
    
    public queryProduct_V2(){
        //This code is used to collect the product family field and the list of items in its picklist
        noBookError = True;
        string quoteId =  ApexPages.currentPage().getparameters().get('Id');
        //Error handeling items, if needed.
        string headerdata= ApexPages.currentPage().getHeaders().get('Host');
        string url='https://' + headerdata;//+ urlvalue; 
        
        //Getting list of product familys to show user. This list from the Product table, family field.
        family_name = new List<SelectOption>();
        
        Schema.DescribeFieldResult fieldResult = Product2.Family.getDescribe();
        List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues();
        //System.debug(ple);
        for( Schema.PicklistEntry f : ple){
            family_name.add(new SelectOption(f.getLabel(), f.getValue()));
        } 
        //ending here, clean up requried
    }  
    
    public PageReference submit() {
        List<QuotedProduct__c> qItems = new List<QuotedProduct__c>();
        for (ProductWrapper wrapper : this.PBEs) {
            if(wrapper.selected == true) {
                qItems.add(new QuotedProduct__c(
                    Quote__c = myQuote[0].Id,
                    CurrencyIsoCode = myQuote[0].CurrencyIsoCode,
                    SubscriptionTermYears__c = myQuote[0].Subscription_Length_Years__c,
                    Price_Book__c = myQuote[0].Opportunity__r.Pricebook2.ID,
                    Product__c = wrapper.PBE.Product2ID,
                    Product_Family__c = wrapper.PBE.Product2.Family,
                    ListPrice__c = wrapper.PBE.UnitPrice,
                    ListUnitPrice__c = wrapper.PBE.UnitPrice,
                    NetUnitPrice__c = wrapper.PBE.UnitPrice,
                    Starting_Price__c = wrapper.PBE.UnitPrice,
                    ProductCode2__c = wrapper.PBE.Product2.ProductCode,
                    ProductDescription2__c = wrapper.PBE.Product2.Description,
                    BillingType__c = wrapper.PBE.Product2.BillingType__c,
                    Quantity__c = 1
                ));
            }
        }
        try{
            if (qItems.size() > 0) {
                insert qItems;
            }
        } catch (System.DmlException e){
            System.debug('Error: Creating the Quoted Items records' + e);
        }
        
        //Send user back to the new quoted Product record
        PageReference pageRef = new PageReference('/' + myQuote[0].Id);
        pageRef.setRedirect(true);
        return pageRef;
    }
    //End testing code again
    
    public void clear(){
        selected_Pricebook = null;
    }
}

The unit test that isn't working. The only output is that the wrapper : this.PBEs is empty, which is correct, as I don't know how to mark a checkbox as selected. I have something there, that doesn't give any errors during compile, but obviously work.

    static testMethod void TestuseProducts() {
        // Setup test data
        //Pricebooks
        List<Pricebook2> pbs2 = new List<Pricebook2>();
        pbs2.add(new Pricebook2(Name='Unit Test PB',IsActive=TRUE));
        //Insert pricebook.
        insert pbs2;
        
        //Sample account for testing
        Account acct = new Account(Name='Query Product Unit Test Account',CurrencyIsoCode='USD');
        insert acct;

        //Sample opportunity for testing
        Opportunity opp = new Opportunity(Pricebook2Id = pbs2[0].Id,Name='Query Product Unit Test',AccountId=acct.Id,CurrencyIsoCode='USD',Stagename='Prospecting',CloseDate=System.today() + 5);
        insert opp;
        
        //Sample quote for testing
        Quote__c q = new Quote__c(Account__c=acct.Id,CurrencyIsoCode='USD',Renewal_Term__c='One Year',Opportunity__c=opp.Id);
        insert q;
        
        //Sample product for testing
        Product2 p2 = new Product2(Name='UnitTest Product',CurrencyIsoCode='USD',IsActive=TRUE,Product_Type__c='Product',BillingType__c='Subscription',Family='XD');
        insert p2;
        
        // Get standard price book ID.
        Id pricebookId = Test.getStandardPricebookId();
        
        // Insert a price book entry for the standard price book.
        PricebookEntry standardPrice = new PricebookEntry(Pricebook2Id = pricebookId, Product2Id = p2.Id, UnitPrice = 10000, IsActive = true);
        insert standardPrice;
        
        //Need to have some pricebook entries for this unit test as well
        List<PricebookEntry> pbe = new List<PricebookEntry>();
        pbe.add(new PricebookEntry(Product2Id=p2.Id, Pricebook2Id=pbs2[0].Id,UnitPrice=12.00,IsActive=True,UseStandardPrice=False));
        //Insert pricebook entry
        insert pbe;
        
        // Phase 1: Setup for useProducts test  
        Test.startTest();
        Pagereference testPage = Page.queryProduct_V2_p;
        testPage.getParameters().put('id', q.Id);
        queryProduct_V2 testController = new queryProduct_V2();            
        test.setCurrentPageReference(testPage);
        List<Quote__c> results = queryProduct_V2.getQuoteDetails();
        testController.selected_Pricebook = 'XD';
        queryProduct_V2.getQuoteDetails();
        
        // Phase 2: trigger the code execution
        testController.getProducts();
        testPage.getParameters().put('wrapper.selected', 'TRUE');
        testController.submit();
        Test.stopTest();

        // Phase 3: useProduct gather results and assert
        List<QuotedProduct__c> test001 =[select id from QuotedProduct__c where Quote__c=:q.id];
        System.assertequals(1,test001.size());
    }

Getting better with APEX coding, but I still have more to learn, so any help on this would be much appreciated.

0

There's a lot code there, so forgive me if this answer is off in some way :-). From what I read, I think the key concept here is that getParameters() is used only to retrieve the map of GET and POST parameters that are passed to the page at the time that it is requested. Those parameters are passed to the server, into the apex controller, and are made available there. This is why setting the "id" parameter works; it's a simple string that's passed to the initial page.

With the PBEs wrapper collection however, this is data that is generated by activity on the page; it isn't passed through a URL parameter. So in your test code, you have to populate that list the way you would in normal code -- not through a string parameter, but rather by instantiating a new object.

testController.PBEs = new List<QueryProduct_V2.ProductWrapper>();
testController.PBEs.push(new QueryProduct_V2.ProductWrapper());
testController.PBEs[0].selected = true;

So effectively you're setting the appropriate state of the controller, rather than modifying the state of the page. Visualforce pages themselves don't need to be unit tested; only their apex controllers do.

3
  • Thanks for the help with this. I think you are hitting on my problem directly. I am not sure how to trigger this controller and get the data it needs loaded, so that it can be updated. In my, albeit limited understanding, the wrapper PBEs is built by getting the list of products in the getProducts function. This doesn't seem to be what is happening. I have tried adding the lines you listed and it gives me the error of Invalid Type: ProductWrapper for the first two lines. Not sure why that is the case. This is the same way that the getProducts starts that wrapper function. – PHonnold Oct 7 '20 at 16:26
  • 1
    Marking this as the answer as it got me going in the right direction. I now have a unit test that is getting me 86% code coverage. – PHonnold Oct 7 '20 at 20:26
  • Thanks, sorry; just saw your prior comment. I should have written QueryProduct_V2.ProductWrapper since it's an inner class. Will update the answer – Nathanael Schmolze Oct 8 '20 at 17:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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