function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
CallMeHugoCallMeHugo 

Unit testing atomic method execution FOR UPDATE

Hi all,

I have a written a method to allocate MAC addresses based on a custom setting that holds the min, max and current pointer. To make sure I never have duplicates and that the method is atomic, I have use the FOR UPDATE keywords on my query. Now I would like to validate the behaviour using a unit test but I am not able to reproduce multiple queries arriving at the same time to force a collision. Here is some simplified code:

public static String GetMAC() {
   string mac;
   MACAddressBank__c bank = [SELECT CurrentMAC__c FROM MACAddressBank__c FOR UPDATE];
   mac = bank.CurrentMAC__c;
   bank.CurrentMAC__c++;    // Assuming you can do this - for simplicity of the example
   UPDATE bank;
}

//// Test
static testMethod void Test_ValidateCollisionMACBank() {
        INSERT new MACAddressBank__c(Name = 'bank1',  CurrentMAC__c = '0123456789AB' );

   Test.startTest();
   Integer size = 1000;
   // Create asynchhronous requests that will retreive macs at the same time
    RequestMACBatchAsync( MAClist1, size );
    RequestMACBatchAsync( MAClist2, size );
    RequestMACBatchAsync( MAClist3, size );
    RequestMACBatchAsync( MAClist4, size );
   Test.stopTest(); // Wait for async methods to finish

   // Do some logic with asserts and check if all lists contains consecutive MACs
   => Always true, !!! Should not, I want to reproducr collision!

   // Do some logic to check if duplicates exists across each lists
  => Always false, OK
}

static List<String> MAClist1 = new List<String>();
static List<String> MAClist2 = new List<String>();
static List<String> MAClist3 = new List<String>();
static List<String> MAClist4 = new List<String>();

@future
private static void RequestMACBatchAsync( List<String> macs, Integer qty ) {
      while( macs.size() < qty ) {
           macs.add(GetMAC());
      }
    }


So the problem is that I am not able to reproduce two calls to GetMAC at the same time.
I also tried to modify the GetMAC method to retreive a fair amount of MACs at the same time but the test always return consecutive MACs in each lists.

This is a business critical process and major problems can happen if a MAC is duplicated for the end user. I really want to make sure that my lock is working correctly. Any ideas on how I can mock this?

Thanks.
Best Answer chosen by CallMeHugo
Ashish_SFDCAshish_SFDC
Hi , 


See the blog below which has good code, 

Here is the apex test class, if you want to experiment.

public class TestRecordLocking {
  testmethod public static void testLocking() {
    // create a account from logged in user
    Account accoutU1 = new Account(Name = 'Abhinav Gupta');
    insert accoutU1;
    // lock the same account  
    accoutU1 = [Select Id, Name, Website from Account where Id =:accoutU1.id for update];
    
    //Start updating it
    accoutU1.website = 'www.salesforce.com';
    
    // Now lets say, some other user comes and tries to update the same record
    User u2 = [Select Id from user where id !=:UserInfo.getUserId() limit 1];
    System.runAs(u2) {
      // other user queries the same account
      Account accountU2 = [Select Id, Name, Website from Account where Id =:accoutU1.id ];
      // other user tries to update it
      accountU2.WebSite = 'www.yahoo.com';
      update accountU2;    
    }
    
    // Now update the original account back.
    update accoutU1;  
  }
}

http://www.tgerm.com/2011/04/visualizing-record-locking-in-soql-ie.html


Regards,
Ashish