Recently, our tests started to fail randomly with the dreaded UNABLE_TO_LOCK_ROW
. The only fix seems to "Restart on Failed" the pipeline until it succeeds.
This is the fixture code in question, line 31 is insert prods
public static List<Product2> InsertProductsWithStandardPrices() {
List<Product2> prods = new List<Product2>();
prods.add(CreateProduct('Test Base Product 1', 'A-1', 'Base Product', 'Each'));
prods.add(CreateProduct('Test Base Product 2', 'A-2', 'Base Product', 'Each'));
prods.add(CreateProduct('Test Base Product 3', 'A-3', 'Base Product', 'Each'));
prods.add(CreateProduct('Test Service Product 1', 'C-1', 'Service', 'per Hour'));
prods.add(CreateSubscriptionProduct('Test License (Yearly, 24 months)', 'B-1', 'Yearly', '24 months'));
prods.add(CreateSubscriptionProduct('Test License (Yearly, 12 months)', 'B-2', 'Yearly', '12 months'));
prods.add(CreateSubscriptionProduct('Test License (Monthly, 12 months)', 'B-3', 'Monthly', '12 months'));
prods.add(CreateSubscriptionProduct('Test License (Monthly, 1 month)', 'B-4', 'Monthly', '1 month'));
prods.add(CreateSubscriptionProduct('Test License (One-Time, 5 yrs)', 'B-6', 'One Time', '60 months', false));
prods.add(CreateSubscriptionProduct('Test License (One-Time, 8 yrs)', 'B-6', 'One Time', '96 months', false));
prods.add(CreateSubscriptionProduct('Test License (One-Time, Lifetime)', 'B-7', 'One Time', 'Lifetime', false));
insert prods;
InsertStandardPrices(prods);
return prods;
}
The InsertStandardPrices
method internally uses Test.getStandardPricebookId
, but since it is not even reached, I assume the error is not with the pricebook setup.
This fixture is used in roughly 25 of our E2E test classes (in @TestSetup
). Currently, we run slightly above 1100 apex tests, roughly 150-200 of them are E2E tests. Our Unit Tests --by definition-- are not allowed to use DML (we use mocks and stubs instead), but end-to-end tests --by definition-- cannot use a mocked selector layer (wouldn't qualify to be an end to end test, then).
When I researched the topic, most questions were almost 8 years old, and the solutions only included:
- Disable
seeAllData
- Disable parallel testing
- Use
FOR UPDATE
For obvious reasons, our tests do not use seeAllData=true
, so this solution does not apply. We use either mocks or fixtures like this one.
Disabling parallel testing is not an option. The last time I tried to disable parallel for all E2E tests (isParallel=false
), a test run that executes only slightly over 500 unit tests (usually completes in less than 3 minutes) took 30 minutes (!) and then timed out.
If code updates products in our tests, it already uses FOR UPDATE
in the SOQL. We do not have any logic in our production code that updates products. We have lot's of fixtures that create accounts, contacts, orders, opportunities, service contracts, etc. None of them ever failed with this error. This only happens on the Product-fixtures.
I'm running out of ideas here. Anybody has a suggestion how to fix this?
- Stop code reuse in fixtures and create duplicated fixtures?
- Use try/catch to retry the insert at least 2 or 3 times?
Appreciate any suggestion!