4

Our tests are scheduled to be run in production on a daily basis. They usually starts let's say one hour before midnight and finishes one hour after midnight. And from time to time we are experiencing random test failures of random test methods, i.e. every time it's another test method, different class, part of business logic etc. (or if we are lucky everything executes successfully).

The only pattern we were able to identify is that failure always relates to invalid value of some date fields or formulas which relies on date calculation.

Our best guess: test method starts executing one day (one second before midnight and some test data are created using System.today() for some fields, business logic runs) and then results assertion happens next day (i.e. System.today() points to another date already).

We've tried to fix this by catching today's date at the beginning of the test method execution and kinda freeze it using wrappers/mocks/proxy whatever we could call it. As result failures almost disappeared (just 1-2 per week). The problem is that we have a lot of formulas in the system which uses TODAY() value and we can not mock it with our custom solution (same way as we did inside apex test code).

Is there anyway to mock date and time at the system/global level? Or maybe there is better idea how to handle this date/time bombs?

Rescheduling tests execution is not an option, cause according to our clients they have no control over it, i.e. no options to specify execution time in their test execution solution(

Bellow is the example of test method which could trigger such midnight problem (no even business logic here):

// executes before midnight: today = Aug 1
Account originalAccount =
    new Account(
        External_Date__c = System.today()
    )
;


insert originalAccount;


Test.startTest();

Test.stopTest();

// executes after midnight: : today = Aug 2
Account actualAccount = [ SELECT Id, External_Date__c, Internal_Formula_Date__c FROM Account WHERE Id = :originalAccount.Id ];

// fails because Aug 1 != Aug 2; but we can mock this in our code
System.assert(System.today(), actualAccount.External_Date__c);

// fails as well. Internal_Formula_Date__c is defined as TODAY(), so we can not mock this!!!
System.assert(System.today(), actualAccount.Internal_Formula_Date__c);
8
  • 1
    Instead of trying some hack I suggest you to check the code and see if you can handle this at code level as well. Aug 1, 2016 at 13:25
  • 3
    nice point, @TusharSharma! one thing to remember -- if test fails sometimes in midnight, that mean that some business logic may fail in midnight, so be careful around that
    – kurunve
    Aug 1, 2016 at 13:27
  • @TusharSharma Updated my question with test method example. Let's leave business logic aside.
    – wesaw
    Aug 1, 2016 at 13:43
  • Can you please add the code of Internal_Formula_Date__c formula field, as this can also be mocked Aug 1, 2016 at 14:15
  • Have you considered asserting against the CreatedDate of the Account instead of system.today()?
    – Derek F
    Aug 1, 2016 at 14:28

2 Answers 2

5

For Apex code, change all your references from Date.today() and System.now() to Util.today and Util.now. Where ...

public static Date today {
     get { return today == null ? Date.today() : today; }
     set;
  }
  public static DateTime now {
     get { return now == null ? System.now() : now; }
     set;
  }

For formulas and VRs ..

  1. Create a hierarchical custom setting TestSettings with two fields: MockedToday and MockedNow of appropriate types
  2. Change references in formula fields from TODAY() to BLANKVALUE($Setup.TestSettings__c.MockToday__c,TODAY()) and equivalent for NOW()

Then, in your test methods - mock the Apex value and the hierarchical settings to "some invariant for the testmethod" date and datetime value

If you have zillions of references to TODAY() in formulas, change only those that affect your testmethods running across midnight threshold.

5
  • Regarding System.today & Utils, yep, that's exactly what we've done, i.e. wrapper around system methods with mocking capabilities. Got your point re formulas & validation rules. Thinking how to minimize the amount of changes, cause don't wanna modify almost every test & formula. Theoretically all formulas with direct on indirect usage of today are impacted, they just happen not to fail by luck (i.e. test method starts and ends at the same day, which could be not the truth for next test run). How nice it would be to have Test.setToday() at the system level, which covers everything, just dreams)
    – wesaw
    Aug 2, 2016 at 6:49
  • One more thing, there are few formulas in the system, which basically can not be modified by adding new stuff. They are too close to 5000 characters limit. That's of course separate problem by itself. It's just make it harder to introduce mocking at the formula's level.
    – wesaw
    Aug 2, 2016 at 6:55
  • BLANKVALUE(..) is a lightweight function but I get your point
    – cropredy
    Aug 2, 2016 at 15:00
  • Registered an idea to introduce date/time mocking support at the system level: success.salesforce.com/ideaView?id=0873A000000lEaPQAU
    – wesaw
    Aug 10, 2016 at 9:02
  • @wesaw - nicely done, have you seen this? andyinthecloud.com/2016/07/30/… - Looks like you adhered to it well
    – cropredy
    Aug 10, 2016 at 14:12
1

I do not know how many system date related formula fields you are using. This could be the workaround for formula field.

Create 2 fields:

Is_Testing__c boolean field (default false) and from your test method, make it TRUE.

Test_System_Date__cdate field, which you can assign value from test method.

Change Internal_Formula_Date__c like this:

IF(Is_Testing__c,Test_System_Date__c,TODAY())

So in your test method it will give you expected value.

2
  • Got your point. Thanks. Unfortunately, there are dozens of fields like that on different objects; formulas are sometimes really complex inside and quite close to length limit (5000 symbols). Thinking how to tune this to make it work at a large scale without a lot of formula updates.
    – wesaw
    Aug 1, 2016 at 18:45
  • Why you are querying the data? rather you can create data in test class and execute test method. Aug 1, 2016 at 18:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .