You need to sign in to do that
Don't have an account?
Trigger No Longer Passes Apex Test After Adding Anti-Recursion
My trigger was throwing some errors when we would try to update opportunities, and it was suggested to me that I create a class to make sure the trigger only runs once, and then reference that class in my trigger. This is the class:
public Class checkRecursive{
private static boolean run = true;
public static boolean runOnce(){
if(run){
run=false;
return true;
}else{
return run;
}
}
}
When I try to test my code coverage for my trigger now, it fails, and I'm not sure why. He's the code for the trigger:
trigger editSchedule on OpportunityLineItem (after insert) {
if(checkRecursive.runOnce())
{
if(Trigger.isInsert){
Map<Id, Opportunity> opportunities = new Map<Id, Opportunity>();
Date myDate = Date.newInstance(1990, 2, 17);
for(OpportunityLineItem oli: Trigger.new) {
opportunities.put(oli.OpportunityId, null);
}
opportunities.putAll([SELECT Contract_Start_Date__c FROM Opportunity WHERE Id in :opportunities.keySet()]);
for(OpportunityLineItem oli: Trigger.new) {
Opportunity thisLineItemOpp = opportunities.get(oli.OpportunityId);
myDate = thisLineItemOpp.Contract_Start_Date__c;
// thisLineItemOpp.CloseDate is the close date you're looking for
}
List<OpportunityLineItemSchedule> listOLIS = new List<OpportunityLineItemSchedule>();
for (OpportunityLineItem oli: Trigger.new){
Set<Id> setOpptyOLI = new set<Id>();
setOpptyOLI.add(oli.Id);
List<OpportunityLineItem> OpptyOlilst=[Select Product2.Family FROM OpportunityLineItem WHERE Id IN:setOpptyOLI ];
Decimal DiscountedPrice;
DiscountedPrice = oli.Discounted_Price__c;
if(oli.Payment_Terms__c == 'Monthly'){
for(OpportunityLineItem record : OpptyOlilst){
if(record.Product2.Family=='Services'){
for(Integer duration = (Integer)oli.Duration__c; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity/oli.Duration__c, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addMonths(1);
}//end of loop
}//end of inner IF
else {
for(Integer duration = (Integer)oli.Duration__c; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addMonths(1);
}//end of loop
}//end of else
}//end of OpptyOlilstForLoop
}//end of Monthly IF
else if(oli.Payment_Terms__c == 'Quarterly'){
for(OpportunityLineItem record : OpptyOlilst){
if(record.Product2.Family=='Services'){
for(Integer duration = (Integer)oli.Duration__c/3; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity*3/oli.Duration__c, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addMonths(3);
}//end of loop
}//end of inner IF
else{
for(Integer duration = (Integer)oli.Duration__c/3; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity*3, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addMonths(3);
}//end of loop
}//end of else
}//end of OpptyOlilstForLoop
}//end of Quarterly IF
else if(oli.Payment_Terms__c == 'Annually'){
for(OpportunityLineItem record : OpptyOlilst){
if(record.Product2.Family=='Services'){
for(Integer duration = (Integer)oli.Duration__c/12; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity*12/oli.Duration__c, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addYears(1);
}//end of loop
}//end of inner IF
else{
for(Integer duration = (Integer)oli.Duration__c/12; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity*12, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addYears(1);
}//end of loop
}//end of else
}//end of OpptyOlilstForLoop
}//end of Annually IF
else if(oli.Payment_Terms__c == 'Up Front'){
for(OpportunityLineItem record : OpptyOlilst){
if(record.Product2.Family=='Services'){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity, ScheduleDate = myDate, Type = 'Revenue'));
}//end of inner IF
else{
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity*oli.Duration__c, ScheduleDate = myDate, Type = 'Revenue'));
}//end of else
}//end of OpptyOlilstForLoop
}//end of Up Front IF
insert listOLIS;
}//end of isInsert
}//end of runOnce
}//end of checkRecursive
}//end of trigger
As you can see, my trigger now references the class I wrote to avoid recursion.
The first time I tested the trigger, I got the following exception:
System.DmlException: Insert failed. First exception on row 0; first error: UNABLE_TO_LOCK_ROW, unable to obtain exclusive access to this record: []
Class.testEditSchedule.testUpdateSchedule: line 48, column 1
The second time, I tested it by itself, and got this exception:
System.AssertException: Assertion Failed: Expected: true, Actual: false
Class.testEditSchedule.testUpdateSchedule: line 112, column 1
Lastly, here's the code for my test class:
@isTest (seeAllData=true)
private class testEditSchedule{
static testMethod void testUpdateSchedule() {
List<Account> aList = new List<Account>();
Account a = new Account(
Name = 'Test Account'
);
aList.add(a);
insert aList;
List<Opportunity> oList = new List<Opportunity>();
Opportunity o = new Opportunity(
Name = 'Test Opportunity',
StageName = 'Discovery',
CloseDate = Date.today(),
Contract_Start_Date__c = Date.today(),
AccountId = a.Id
);
oList.add(o);
insert oList;
//Create Product
Product2 prod = new Product2();
prod.IsActive = true;
prod.Name = 'Onboarding';
prod.CanUseRevenueSchedule=true;
insert prod;
//Create PriceBook
Pricebook2 pb = new Pricebook2();
pb.Name = 'Test';
pb.IsActive = true;
insert pb;
List<Pricebook2> PbList = [Select Id, Name, IsActive From Pricebook2 where IsStandard = true LIMIT 1];
//Pricebook2 standardpb = [Select Id, Name, IsActive From Pricebook2 where IsStandard = true LIMIT 1];
PricebookEntry standardpbe = new PricebookEntry();
standardpbe.Pricebook2Id = PbList[0].Id;
standardpbe.Product2Id = prod.Id;
standardpbe.UnitPrice = 10000;
standardpbe.IsActive = true;
standardpbe.UseStandardPrice = false;
insert standardpbe;
PricebookEntry pbe = new PricebookEntry();
pbe.Pricebook2Id = pb.Id;
pbe.Product2Id = prod.Id;
pbe.UnitPrice = 10000;
pbe.IsActive = true;
pbe.UseStandardPrice = false;
insert pbe;
List<OpportunityLineItem> oliList = new List<OpportunityLineItem>();
List<OpportunityLineItem> oliAnnuallyList = new List<OpportunityLineItem>();
List<OpportunityLineItem> oliQuarterlyList = new List<OpportunityLineItem>();
List<OpportunityLineItem> oliMonthlyList = new List<OpportunityLineItem>();
OpportunityLineItem oliAnnually = new OpportunityLineItem(
OpportunityId = o.Id,
PricebookEntryId = pbe.id,
TotalPrice = 1,
Quantity=2,
Payment_Terms__c ='Annually',
Duration__c = 12
);
OpportunityLineItem oliMonthly= new OpportunityLineItem(
OpportunityId = o.Id,
PricebookEntryId = pbe.id,
TotalPrice = 1,
Quantity=2,
Payment_Terms__c ='Monthly',
Duration__c = 1
);
OpportunityLineItem oliQuarterly= new OpportunityLineItem(
OpportunityId = o.Id,
PricebookEntryId = pbe.id,
TotalPrice = 1,
Quantity=2,
Payment_Terms__c ='Quarterly',
Duration__c = 3
);
OpportunityLineItem oli= new OpportunityLineItem(
OpportunityId = o.Id,
PricebookEntryId = pbe.id,
TotalPrice = 1,
Quantity=2,
Payment_Terms__c = 'Up Front',
Duration__c = 12
);
oliAnnuallyList.add(oliAnnually);
oliMonthlyList.add(oliMonthly);
oliQuarterlyList.add(oliQuarterly);
oliList.add(oli);
insert oliList;
insert oliAnnuallyList;
insert oliQuarterlyList;
insert oliMonthlyList;
//Your trigger should have OpportunityLineItemSchedule logic then following asert will pass else it will fail
List<OpportunityLineItem> lstOptyLi =[SELECT HasRevenueSchedule from OpportunityLineItem where Id =:oli.id];
System.assertEquals(True, lstOptyLi[0].HasRevenueSchedule);
List<OpportunityLineItem> lstOptyLi_2 =[SELECT HasRevenueSchedule from OpportunityLineItem where Id =:oliAnnually.id];
System.assertEquals(True, lstOptyLi_2[0].HasRevenueSchedule);
List<OpportunityLineItem> lstOptyLi_3 =[SELECT HasRevenueSchedule from OpportunityLineItem where Id =:oliQuarterly.id];
System.assertEquals(True, lstOptyLi_3[0].HasRevenueSchedule);
List<OpportunityLineItem> lstOptyLi_4 =[SELECT HasRevenueSchedule from OpportunityLineItem where Id =:oliMonthly.id];
System.assertEquals(True, lstOptyLi_4[0].HasRevenueSchedule);
}
}
I'm not sure why adding that little bit of code caused my test to stop working, but it thinks my trigger is messed up now. Can someone help me figure out how to fix it?
Thanks
public Class checkRecursive{
private static boolean run = true;
public static boolean runOnce(){
if(run){
run=false;
return true;
}else{
return run;
}
}
}
When I try to test my code coverage for my trigger now, it fails, and I'm not sure why. He's the code for the trigger:
trigger editSchedule on OpportunityLineItem (after insert) {
if(checkRecursive.runOnce())
{
if(Trigger.isInsert){
Map<Id, Opportunity> opportunities = new Map<Id, Opportunity>();
Date myDate = Date.newInstance(1990, 2, 17);
for(OpportunityLineItem oli: Trigger.new) {
opportunities.put(oli.OpportunityId, null);
}
opportunities.putAll([SELECT Contract_Start_Date__c FROM Opportunity WHERE Id in :opportunities.keySet()]);
for(OpportunityLineItem oli: Trigger.new) {
Opportunity thisLineItemOpp = opportunities.get(oli.OpportunityId);
myDate = thisLineItemOpp.Contract_Start_Date__c;
// thisLineItemOpp.CloseDate is the close date you're looking for
}
List<OpportunityLineItemSchedule> listOLIS = new List<OpportunityLineItemSchedule>();
for (OpportunityLineItem oli: Trigger.new){
Set<Id> setOpptyOLI = new set<Id>();
setOpptyOLI.add(oli.Id);
List<OpportunityLineItem> OpptyOlilst=[Select Product2.Family FROM OpportunityLineItem WHERE Id IN:setOpptyOLI ];
Decimal DiscountedPrice;
DiscountedPrice = oli.Discounted_Price__c;
if(oli.Payment_Terms__c == 'Monthly'){
for(OpportunityLineItem record : OpptyOlilst){
if(record.Product2.Family=='Services'){
for(Integer duration = (Integer)oli.Duration__c; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity/oli.Duration__c, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addMonths(1);
}//end of loop
}//end of inner IF
else {
for(Integer duration = (Integer)oli.Duration__c; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addMonths(1);
}//end of loop
}//end of else
}//end of OpptyOlilstForLoop
}//end of Monthly IF
else if(oli.Payment_Terms__c == 'Quarterly'){
for(OpportunityLineItem record : OpptyOlilst){
if(record.Product2.Family=='Services'){
for(Integer duration = (Integer)oli.Duration__c/3; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity*3/oli.Duration__c, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addMonths(3);
}//end of loop
}//end of inner IF
else{
for(Integer duration = (Integer)oli.Duration__c/3; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity*3, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addMonths(3);
}//end of loop
}//end of else
}//end of OpptyOlilstForLoop
}//end of Quarterly IF
else if(oli.Payment_Terms__c == 'Annually'){
for(OpportunityLineItem record : OpptyOlilst){
if(record.Product2.Family=='Services'){
for(Integer duration = (Integer)oli.Duration__c/12; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity*12/oli.Duration__c, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addYears(1);
}//end of loop
}//end of inner IF
else{
for(Integer duration = (Integer)oli.Duration__c/12; duration > 0; duration--){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity*12, ScheduleDate = myDate, Type = 'Revenue'));
myDate = myDate.addYears(1);
}//end of loop
}//end of else
}//end of OpptyOlilstForLoop
}//end of Annually IF
else if(oli.Payment_Terms__c == 'Up Front'){
for(OpportunityLineItem record : OpptyOlilst){
if(record.Product2.Family=='Services'){
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity, ScheduleDate = myDate, Type = 'Revenue'));
}//end of inner IF
else{
listOLIS.add(new OpportunityLineItemSchedule(OpportunityLineItemId = oli.Id, Revenue = DiscountedPrice*oli.Quantity*oli.Duration__c, ScheduleDate = myDate, Type = 'Revenue'));
}//end of else
}//end of OpptyOlilstForLoop
}//end of Up Front IF
insert listOLIS;
}//end of isInsert
}//end of runOnce
}//end of checkRecursive
}//end of trigger
As you can see, my trigger now references the class I wrote to avoid recursion.
The first time I tested the trigger, I got the following exception:
System.DmlException: Insert failed. First exception on row 0; first error: UNABLE_TO_LOCK_ROW, unable to obtain exclusive access to this record: []
Class.testEditSchedule.testUpdateSchedule: line 48, column 1
The second time, I tested it by itself, and got this exception:
System.AssertException: Assertion Failed: Expected: true, Actual: false
Class.testEditSchedule.testUpdateSchedule: line 112, column 1
Lastly, here's the code for my test class:
@isTest (seeAllData=true)
private class testEditSchedule{
static testMethod void testUpdateSchedule() {
List<Account> aList = new List<Account>();
Account a = new Account(
Name = 'Test Account'
);
aList.add(a);
insert aList;
List<Opportunity> oList = new List<Opportunity>();
Opportunity o = new Opportunity(
Name = 'Test Opportunity',
StageName = 'Discovery',
CloseDate = Date.today(),
Contract_Start_Date__c = Date.today(),
AccountId = a.Id
);
oList.add(o);
insert oList;
//Create Product
Product2 prod = new Product2();
prod.IsActive = true;
prod.Name = 'Onboarding';
prod.CanUseRevenueSchedule=true;
insert prod;
//Create PriceBook
Pricebook2 pb = new Pricebook2();
pb.Name = 'Test';
pb.IsActive = true;
insert pb;
List<Pricebook2> PbList = [Select Id, Name, IsActive From Pricebook2 where IsStandard = true LIMIT 1];
//Pricebook2 standardpb = [Select Id, Name, IsActive From Pricebook2 where IsStandard = true LIMIT 1];
PricebookEntry standardpbe = new PricebookEntry();
standardpbe.Pricebook2Id = PbList[0].Id;
standardpbe.Product2Id = prod.Id;
standardpbe.UnitPrice = 10000;
standardpbe.IsActive = true;
standardpbe.UseStandardPrice = false;
insert standardpbe;
PricebookEntry pbe = new PricebookEntry();
pbe.Pricebook2Id = pb.Id;
pbe.Product2Id = prod.Id;
pbe.UnitPrice = 10000;
pbe.IsActive = true;
pbe.UseStandardPrice = false;
insert pbe;
List<OpportunityLineItem> oliList = new List<OpportunityLineItem>();
List<OpportunityLineItem> oliAnnuallyList = new List<OpportunityLineItem>();
List<OpportunityLineItem> oliQuarterlyList = new List<OpportunityLineItem>();
List<OpportunityLineItem> oliMonthlyList = new List<OpportunityLineItem>();
OpportunityLineItem oliAnnually = new OpportunityLineItem(
OpportunityId = o.Id,
PricebookEntryId = pbe.id,
TotalPrice = 1,
Quantity=2,
Payment_Terms__c ='Annually',
Duration__c = 12
);
OpportunityLineItem oliMonthly= new OpportunityLineItem(
OpportunityId = o.Id,
PricebookEntryId = pbe.id,
TotalPrice = 1,
Quantity=2,
Payment_Terms__c ='Monthly',
Duration__c = 1
);
OpportunityLineItem oliQuarterly= new OpportunityLineItem(
OpportunityId = o.Id,
PricebookEntryId = pbe.id,
TotalPrice = 1,
Quantity=2,
Payment_Terms__c ='Quarterly',
Duration__c = 3
);
OpportunityLineItem oli= new OpportunityLineItem(
OpportunityId = o.Id,
PricebookEntryId = pbe.id,
TotalPrice = 1,
Quantity=2,
Payment_Terms__c = 'Up Front',
Duration__c = 12
);
oliAnnuallyList.add(oliAnnually);
oliMonthlyList.add(oliMonthly);
oliQuarterlyList.add(oliQuarterly);
oliList.add(oli);
insert oliList;
insert oliAnnuallyList;
insert oliQuarterlyList;
insert oliMonthlyList;
//Your trigger should have OpportunityLineItemSchedule logic then following asert will pass else it will fail
List<OpportunityLineItem> lstOptyLi =[SELECT HasRevenueSchedule from OpportunityLineItem where Id =:oli.id];
System.assertEquals(True, lstOptyLi[0].HasRevenueSchedule);
List<OpportunityLineItem> lstOptyLi_2 =[SELECT HasRevenueSchedule from OpportunityLineItem where Id =:oliAnnually.id];
System.assertEquals(True, lstOptyLi_2[0].HasRevenueSchedule);
List<OpportunityLineItem> lstOptyLi_3 =[SELECT HasRevenueSchedule from OpportunityLineItem where Id =:oliQuarterly.id];
System.assertEquals(True, lstOptyLi_3[0].HasRevenueSchedule);
List<OpportunityLineItem> lstOptyLi_4 =[SELECT HasRevenueSchedule from OpportunityLineItem where Id =:oliMonthly.id];
System.assertEquals(True, lstOptyLi_4[0].HasRevenueSchedule);
}
}
I'm not sure why adding that little bit of code caused my test to stop working, but it thinks my trigger is messed up now. Can someone help me figure out how to fix it?
Thanks
Travis - I believe this is because all of those list insertions occur within the same context, which means your recursive check is set to false after you insert oliList, and then it remains false as you're inserting the remaining lists. I believe that if you break your test into four seperate test methods - one for each time period - the recursive check will reset between each and your test will pass.