1

I am writting Apex Test Class, but facing the issue, of unable to query PricebookEntry.

Actual Error: (System.QueryException: List has no rows for assignment to SObject)

Here's the logic in a simplified form:

  1. When I create an opportunity I will automatically assign it a Pricebook2 (Standard Pricebook). (Via Flow)
  2. Which will be brought down to Quote created related to the Opportunity. (via Flow)

Another logic is:
When I create a new product, there's a flow that runs automatically to create a new PricebookEntry in the Standard Pricebook.

This is the code snippet in the test class:

@isTest
static void testGetQuoteLineItems() {

RecordType rt = [SELECT Id, Name FROM RecordType WHERE (sObjectType = 'Opportunity' AND Name = 'XX')];
        
Opportunity opp = new Opportunity(Name='Test Opp', StageName='Prospecting', CloseDate=Date.today(), RecordTypeId=rt.Id);
insert opp;
        
Quote q = new Quote(Name='Test Quote', OpportunityId=opp.Id, ExpirationDate=Date.today().addDays(10), Status='Draft');
insert q;
        
Product2 prod1 = new Product2(Name='To Be Deleted 2');
Product2 prod2 = new Product2(Name='To Be Deleted 3');
insert new List<Product2>{prod1, prod2};
    
PricebookEntry pbe1 = [SELECT Id FROM PricebookEntry WHERE Product2Id =: prod1.Id];
PricebookEntry pbe2 = [SELECT Id FROM PricebookEntry WHERE Product2Id =: prod2.Id];
}

When this lines execute it breaks at the PricebookEntry pbe1... line onwards with the error List has no rows for assignment. I have checked the debug logs too, the flow which runs after product2 record is created (which creates PricebookEntry in Standard Pricebook) runs before the PricebookEntry pbe1.... line is executed.

However, if I execute this in Anonymous window, I could perfectly get the pbe record.

Is there anything I am missing here, in creating test class for pricebookentry? Any help is greatly appreciated, many thanks in advance!

1 Answer 1

2

If you're not using @isTest(SeeAllData=true), your test cannot actually query the standard price book. The intended route is to call the Test.getStandardPricebookId method. You're going to need at least a small amount of Apex to call this method. I'd probably recommend an InvocableMethod to determine if the standard price book can be queried, and if not, use the method above to get and return the standard price book.

public class StandardPricebook {
    public class Input {
        @InvocableVariable public String unusedProperty;
    }
    static Id pricebookId;
    @InvocableMethod static public Id[] getStandardPricebookId(Input[] inputs) {
        Id[] results = new Id[0];
        if(pricebookId == null) {
            Pricebook2[] standardPricebook = [SELECT Id FROM Pricebook2 WHERE IsStandard = true];
            if(standardPricebook.isEmpty()) {
                standardPricebook.add(new Pricebook2(Id=Test.getStandardPricebookId()));
            }
            pricebookId = standardPricebook[0].Id;
        }
        for(Input input: inputs) {
            results.add(pricebookId);
        }
        return results;
    }
}
1
  • Thank you so much for your answer and clarification. I was surprised to see, I could not even query the Pricebook2Id field in opportunity, even though I specify it to be Test.getStandardPricebook. Your answer has greatly given me the knowledge to understand about Pricebook behaviour in Test Class. However, before trying the code you provided above, I followed Ben Edwards answer from [here] (developer.salesforce.com/forums/?id=906F0000000BIGWIA4). I will try the code you provided and let you know again in the comments. Once again, thank you very much for your answer!
    – prem22
    Commented Aug 6, 2023 at 9:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .