UNABLE_TO_LOCK_ROW Custom Setting Parallel Apex Testing Summer ’13 (28.0) Regression

  1. All of my classes, triggers, pages, components are set to API Version 28.0, Summer ’13.
  2. None of my test classes/methods use SeeAllData=true annotation.
  3. Most of my test classes/methods insert identical test data into a list Custom Setting, as every test starts with a blank slate, and this Custom Setting config is required for testing most parts of my application.

Prior to Summer ’13 (28.0), in Spring ’13 (27.0) and earlier, my test code, which didn’t change, ran just fine in parallel, I never saw the UNABLE_TO_LOCK_ROW Custom Setting contention until Summer ’13 (28.0).

Answer

I had to combat this in our environment when trying to move towards running tests in parallel. I ended up using a centralized static method (e.g. in a test utility class) to configure a given custom setting.

The first thing the method does is check the database for an existing custom setting record filtered by the id of the current user’s context. If I find one then I can proceed to update it if needed, otherwise, I build a new custom setting object and set it up the way I want. The most important thing to note here is the Name field acts as the external id for the table. Make sure you set up each custom setting with a unique name. Finally, upsert the object and use the Name field as the external id.

I can go into some of the theory behind why I think I had to use this method if you want, but I’ll spare you. Hah.

Here’s some code:

public static void setupMyCustomSetting(Boolean value, String name) {
    try {
        My_Custom_Setting__c customSetting = [
            SELECT
                My_Custom_Field__c
            FROM My_Custom_Setting__c
            WHERE SetupOwnerId = :UserInfo.getUserId()
        ];

        customSetting.My_Custom_Field__c = value;
        update customSetting;
    } catch (QueryException ex) {
        My_Custom_Setting__c customSetting = new My_Custom_Setting__c(
            Name = name,
            SetupOwnerId = UserInfo.getUserId(),
            My_Custom_Field__c = value
        );

        Database.upsert(customSetting, My_Custom_Setting__c.Name.getDescribe().getSObjectField(), false);
    }
}

Put this in a test utility class and call it from each test class’s testSetup method (or static block if you’re still using that paradigm). I just pass in the name of the test class into the above method.

Something like:

MyTestUtilityClass.setupMyCustomSetting(false, 'MyTestClass');

Then when you’re writing your unit tests, you can flip the status of your custom setting by calling the same facility. Obviously this is a rudimentary example, but you can get fancy and pass in a more complicated data structure if you wanted.

Let me know if this works out for anyone.

Attribution
Source : Link , Question Author : mjgallag , Answer Author : Bobby Knezevic

Leave a Comment