I've tried to find a case that is relatable to mine with a standard controller and class extension with a test class that is seeing this same issue, but I've been unsuccessful.
I created a custom VF related list that references a standardController with a class extension. The VF related list is on the opportunity, pulling in through a query on the apex class, the synced quote's service (QService__c object) line items. If there are no line items, it displays a message. Here is the VF code to give some context.
<apex:page standardController="Opportunity" extensions="serviceViewController" lightningStyleSheets="true">
<apex:form >
<!-- Display related list if there is a service on synced quote -->
<apex:pageBlock id="section1" rendered="{!section1}" title="Services">
<apex:pageBlockTable value="{!getQServices}" var="qservice">
<!-- ID Column w/ related link -->
<apex:column headerValue="Id">
<apex:outputLink value="{!URLFOR($Action.QService__c.View, qservice.Id)}" title="Id">
{!qservice.Id}
</apex:outputLink>
</apex:column>
<!-- Name Column -->
<apex:column value="{!qservice.Name}"/>
<!-- Total Service Cost Column -->
<apex:column value="{!qservice.Total_Service_Cost__c}"/>
</apex:pageBlockTable>
</apex:pageBlock>
<!-- Display catch error on if no service records on synced quote or no synced quote. -->
<apex:pageBlock id="section2" rendered="{!section2}" title="Services">
<h1>
You need a synced quote with service records for this list to populate.
</h1>
</apex:pageBlock>
</apex:form>
The class and the VF page work just fine. They function as designed. Here is the apex class code.
public class serviceViewController {
public Opportunity opp; //Create opp var
public List<QService__c> getQServices{get; set;} //List to pull in values from Qservice__c for related list.
public boolean section1{get; set;} //used for deciding what to render on VF page.
public boolean section2{get; set;} //used for deciding what to render on VF page.
//Controller Extension
public serviceViewController(ApexPages.StandardController controller) {
this.opp = (Opportunity)controller.getRecord();
//Pull in columns for QService__c and load into getQServices
getQServices = [SELECT Id, Name, Total_Service_Cost__c
FROM QService__c
WHERE Opportunity__c = :opp.Id
AND Synced_Service__c = TRUE
];
//Set to TRUE if there IS services on the synced quote.
boolean recordsExist = ![SELECT Id FROM QService__c WHERE Opportunity__c = :opp.Id AND Synced_Service__c = TRUE LIMIT 1].isEmpty();
//If records do exist, set section1 to TRUE to render the related list, otherwise display catch error VF display.
if(recordsExist == TRUE){
section1 = TRUE;
section2 = FALSE;
} else if(recordsExist == FALSE){
section1 = FALSE;
section2 = TRUE;
}}
}
My issue is with the test class. Looking through the code you can see that it's self-explanatory. An account is created, then a contact, then an opp, with a quote, and then a service record. Then the freshly quote is set as the synced quote.
The test class was able to save, but I was getting 0% code coverage in my class and I realized I should be calling an instance of my APEX class so it can initialize my VF page when the test class is run. You'll see in the syntax the last commented code and it's line. That's where I'm running into an issue. Construct not defined: [serviceViewController].()
@isTest
private class serviceViewControllerTestClass {
public static Opportunity opportunityInstance {get; set;}
@isTest static void createEverything() {
//Create Account
Account acc = new Account();
acc.Name = 'Test Account';
insert acc;
//Create Contact
Contact con = new Contact();
con.FirstName = 'Kruse';
con.LastName = 'Collins';
con.AccountId = acc.Id;
con.LeadSource = 'Event';
con.Contact_Role__c = 'VP';
insert con;
//Create Opportunity
Opportunity opp = new Opportunity();
opp.Name = 'Test Opp';
opp.CloseDate = date.today();
opp.Accountid = acc.Id;
opp.Contact2__c = con.Id;
opp.Stagename = 'Discover and Assess';
opp.Holdout_Opportunity__c = FALSE;
opp.ForecastCategoryName = 'Commit';
opp.Sales_Process_Stage__c = 'Discover and Assess';
opp.Forecast_Percentage_NEW__c = 15.00;
opp.Turn_Off_Initial_CX_EX_RC_Amounts__c = TRUE;
Map<String,Schema.RecordTypeInfo> rtMapByName = Schema.SObjectType.Opportunity.getRecordTypeInfosByName();
//opportunityInstance.RecordTypeId = rtMapByName.get('XM').getRecordTypeId();
insert opp;
//Create Quote
Quote qt = new Quote();
qt.Name = 'Test Sync Quote';
qt.OpportunityId = opp.Id;
qt.License_Term_in_months__c = 12.00;
insert qt;
//Create Service Record
QService__c srvc = new QService__c();
srvc.Name = 'Synced Service';
srvc.Service_Type__c = 'TAM';
srvc.Quote__c = qt.Id;
insert srvc;
opp.SyncedQuoteId = qt.Id;
update opp;
//This is where I'm getting the constructor not defined error
serviceViewController sv = new serviceViewController();
}}
Anything helps with figuring this out. What might be throwing this error? Is it the naming of the extension and the class? Or is it scope? As well, is this even the right direction I'm going with trying to get full coverage of my APEX text class?
serviceViewController
is misleading.