20

Apparently a new feature of the Spring '12 / v24.0 release of Apex in Salesforce.com is that unit tests no longer have access to 'real' data -- thus (if I'm understanding the change correctly) a SOQL query will now only retrieve objects that have been inserted during the course of the unit test -- and even that is subject to some limitations.

At any rate this seems to throw OpportunityLineItem testing out the window, because:

  • It's impossible to insert an OpportunityLineItem without a PriceBookEntryId, BUT
  • You can't insert a new price-book entry for product X unless you already have a Standard Price Book entry for product X, BUT
  • There isn't a Standard Price Book in the test data because the Pricebook2 table, like all tables, is effectively empty at the beginning of the unit-test run, AND
  • There's no way to create a Standard Price Book in Apex

I'm really hoping I got at least one of those four points wrong, but so far no variation on my existing unit-tests has shown any of them to be wrong. Which of course means my unit tests no longer work. This happened literally overnight -- the tests ran fine in my sandbox on Friday, and now they fail.

Am I missing something, or is this a bug in the new SFDC release?

Ben Dunlap
  • 1,838
  • 1
  • 16
  • 17
  • 4
    I posted a similar question on developerforce and learned that a simple workaround for my own purposes is to save the unit-test class as v23.0 or to prefix the 'class' statement with "@isTest (seeAllData=true)". That's fine as far as it goes and helps me keep working today, but v24.0 unit-tests still seem broken to me unless I'm missing something, so I'll leave this question open. – Ben Dunlap Feb 06 '12 at 20:05
  • Can you please provide a link to the question on developerforce? – Daniel Ballinger Feb 16 '12 at 01:37
  • 1
    Sure: http://boards.developerforce.com/t5/Apex-Code-Development/How-do-I-unit-test-opportunity-line-items-in-Spring-12-without-a/m-p/394459 – Ben Dunlap Feb 24 '12 at 18:10

5 Answers5

16

There is new functionality introduced in Summer 14, you can now use Test.getStandardPricebookId() to get the standard pricebook ID without having to set SeeAllData to True.

Jason Hardy
  • 261
  • 2
  • 5
12

Firstly, to put your mind at ease, there are no plans ever to deprecate the seeAllData flag. We're not going to pull the rug out from under you. As to the creation of standard price book in an apex test, I'm not sure. There are, I'm sure, several areas where testing without existing data is difficult on the platform today, which is one reason why the seeAllData flag is there. We'll be trying to close those gaps in the next few releases.

Rich Unger
  • 261
  • 2
  • 2
4

I just ran into this, and although your post is old, it's the first result on Google so I thought I'd share what I did.

My basic architecture is a test class that calls a utility class to as a way of creating test data on the fly (there are other ways, this is my habit).

Short version:

  • set see all data to true
  • make sure the standard price book is active
  • add a pricebook entry for the standard price book - flag as active
  • add a pricebook entry for you test price book - flag as active

Test class:

@isTest (seeAllData=true)
public with sharing class RMA_SelectLineItemsControllerTest {

    static testmethod void testBasicObjects() {
        Pricebook2 standard = [Select Id, Name, IsActive From Pricebook2 where IsStandard = true LIMIT 1];
        if (!standard.isActive) {
            standard.isActive = true;
            update standard;
        }

        Pricebook2 pb = RMA_TestUtilities.createPricebook();
        Product2 prod = RMA_TestUtilities.createProduct();
        PricebookEntry pbe = RMA_TestUtilities.createPricebookEntry(standard,pb,prod);
    }

}

The utility method look like this (only showing that around the new PBE):

public static PricebookEntry createPricebookEntry (Pricebook2 standard, Pricebook2 newPricebook, Product2 prod) {
    System.debug('***** starting one');
    PricebookEntry one = new PricebookEntry();
    one.pricebook2Id = standard.id;
    one.product2id = prod.id;
    one.unitprice = 1249.0;
    one.isactive = true;
    insert one;
    System.debug('***** one complete, ret next');
    PricebookEntry ret = new PricebookEntry();
    ret.pricebook2Id = newPricebook.id;
    ret.product2id = prod.id;
    ret.unitprice = 1250.0;
    ret.isactive = true;
    insert ret;
    return ret;
}
eyescream
  • 18,088
  • 2
  • 34
  • 46
ReidCarlberg
  • 141
  • 1
  • 1
  • 4
  • No probs, it is a valuable piece of info so I've given it +1 too ;) SF docs mention it but it's a bit hidden, you need to know what to look for ;) (http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_testing_data_access.htm) "nserting a pricebook entry for a product isn’t feasible from a test since the standard pricebook isn’t accessible and can’t be created in a running test. Also, inserting a pricebook entry for a custom pricebook isn’t supported since this requires defining a standard pricebook. For such situations, annotate your test method with IsTest(SeeAllData=true)" – eyescream Mar 07 '13 at 07:53
  • It does indeed say that. The advice from earlier in the thread (and elsewhere) confused me. I felt it implied that the running org had to be good to go with a an active, standard pricebook, which is just a bad assumption. I was relieved to find I could do this little update and have it work. Cheers! – ReidCarlberg Mar 07 '13 at 11:32
1

Another work around would be to make your trigger be aware of being run in a test using Test.isRunningTest(), but I think this solution misses the point of best practice, which I believe is the whole point of making tests isolated from pre-existing data.

Perhaps Salesforce could make the Pricebook2.isStandard field writeable if code is running in the context of a test, or the specific Standard Price Book record should be given the same status as User and Profile??

Jev Björsell
  • 891
  • 1
  • 9
  • 25
0
Please let me know if anyone has used  Test.getStandardPricebookId() and able to insert opportunity line item in test class. I tried this method with below mentioned code but got an error ": STANDARD_PRICE_NOT_DEFINED, No standard price defined for this product: []".

Note: I have seeAllData=false

ID standardPBID = Test.getStandardPricebookId();

            PriceBook2 pb = new PriceBook2();
            pb.name = 'GEW Water CMS';
            pb.isActive=true;
            insert pb;

Product2 prod= new Product2();
                prod.name='TestProd';
                prod.productcode='4568';
                prod.isActive=true;
                insert prod;



PricebookEntry standardPrice = new PricebookEntry(Pricebook2Id = standardPBID, Product2Id = prod.Id, UnitPrice = 10000, IsActive = true,  currencyISOCode='USD' );

                PriceBookEntry pbe= new PricebookEntry(pricebook2id=pb.id, product2id=prod.id,unitprice=2000, isActive=true, currencyISOCode='EUR');
                insert pbe;

                OpportunityLineItem oli = new OpportunityLineItem(OpportunityId = OppList[0].Id, pricebookentryid=pbe.id, UnitPrice = 100, Quantity = 1);
                insert oli;