Struggling with building my first test class so I can deploy an Apex trigger.
The trigger converts an Opportunity/Opportunity Products into a Sales Order/Order Items.
Receiving the below error message:
System.DmlException: Insert failed. First exception on row 0; first error: FIELD_INTEGRITY_EXCEPTION, field integrity exception: PricebookEntryId, unknown (versions 3.0 and higher must specify pricebook entry id, others must specify product id): [PricebookEntryId, unknown]
Below is my test class:
@isTest
public class Create_Order_Test_Class {
@isTest static void testSetup(){
Account testAcct = new Account (Name = 'My Test Account');
insert testAcct;
Opportunity oppt = new Opportunity();
oppt.Name ='New Test Deal';
oppt.AccountID = testAcct.Id;
oppt.StageName = 'Closed Won';
oppt.Amount = 24000;
oppt.CloseDate = System.today();
insert oppt;
Order ord = new Order();
ord.OpportunityId = oppt.Id;
ord.AccountId = oppt.AccountId;
ord.EffectiveDate = oppt.CloseDate;
ord.Status= 'Draft';
ord.Pricebook2Id = oppt.Pricebook2Id;
insert ord;
Product2 p = new Product2();
p.ProductCode = 'attribution_annual';
p.Name = 'Attribution - Annual';
insert p;
String pbs = Test.getStandardPricebookId();
List<PricebookEntry> pbes = new List<PricebookEntry>();
pbes.add(new PricebookEntry(Pricebook2Id = pbs, Product2Id = p.Id, UnitPrice = 24000, IsActive = true, UseStandardPrice = false));
insert pbes;
Test.startTest();
Map<String, String> productsMap = new Map<String, String>();
for (PricebookEntry x : [Select Id, Product2.Name from PricebookEntry where Product2.ProductCode in ('Standard Price Book')]){
productsMap.put(x.Product2.Name, x.Id);
}
update testAcct;
update oppt;
OpportunityLineItem oli = new OpportunityLineItem (OpportunityId = oppt.Id, Quantity = 1, UnitPrice = 24000, PricebookEntryId = productsMap.get('Standard Price Book'));
insert oli;
Map<string, string> items = new Map<string, string>();
for(OpportunityLineItem x: [select id,PricebookEntryId from OpportunityLineItem where OpportunityId =: oppt.Id]){
items.put(x.PricebookEntryId, x.id);
OrderItem oi = new OrderItem(OrderId = items.get(oli.OpportunityId), Quantity = oli.Quantity, PricebookEntryId = oli.PricebookEntryId, UnitPrice = oli.UnitPrice);
update oi;
Test.stopTest();
}
}
}
Best Answer
Yeah, working with OpportunityLineItems is one of the more tedious things to test because of all the required setup.
The issue(s)
The immediate issue I see here is with the following line (broken onto separate lines to make it easier to read)
Your
productsMap
keys are the product names, but you never create a product with the name "Standard Price Book". SoproductsMap.get('Standard Price Book')
will returnnull
. Given thatOpportunityLineItem
requires the PricebookEntryId, having that set tonull
is a problem.I suspect that your
productsMap
is actually completely empty, because the query you're using to populate it containswhere Product2.ProductCode in ('Standard Price Book')
. TheProductCode
that you're setting for your test product is "attribution_annual"Fixing things
Basically, you just need to adjust your queries so that you're filtering (i.e. the stuff in the
WHERE
clause) on the appropriate things.Instead of
To fetch the PBEs from a given Pricebook2, you'd want to use
Similarly, when creating your OLI, you need to use the Product Name (because that's how your previous loop built) instead of the Pricebook Name.
instead of
You want
Your work isn't done after you fix these issues
If I'm understanding you correctly, you shouldn't be creating an
Order
or anOrderItem
in your test method at all. That's the work that your trigger is supposeed to be doing, so let the trigger do that. This test is to make sure that the trigger is, in fact, doing that work.Tests have 3 general phases:
I'm guessing that your trigger does this work when the Opportunity stage changes to "Closed Won". So your initial setup should be creating the Product2, PricebookEntry, Opportunity (in a non "closed won" stage), and OpportunityLineItem records.
Then, to actually cause your trigger to do work, you'd change your Opportunity StageName to "Closed Won" and perform a DML update.
Coverage is a side effect of unit tests.
The real utility of tests to us developers is to show that our code is behaving as expected.
To do that, you should be making assertions (
System.assert()
,System.assertEquals()
, orSystem.assertNotEquals()
). You'll generally have multiple assertions in a given test method, and the things that you want to assert against are the results of running your code.In this case, you'd want to query for
Order
andOrderItem
both before you perform the operation that causes your trigger to do the conversion, and after. Your assertions would then be along the lines of: