Mocking BusinessHours records returns System.MathException: null

apexbusinesshoursfflibmockunit-test

I am stuck on a test method and any help would be highly appreciated.

To provide some context: I created a method that queries a specific BusinessHours record on the DB and validates if a Case was created within Business Hours (using isWithin method).

As I cannot specifically create the BusinessHours record during the test setup neither assure that it will exist on every scratch org decided to use fflib's ApexMocks in order to mock the Selector method (as suggested here).

The mocking itself is working, and this is the code I am using to generate a test BusinessHours record:

BusinessHours testBusinessHours = new BusinessHours(
    Id = fflib_IDGenerator.generate(Schema.BusinessHours.SObjectType), 
    Name = 'Test Business Hours',
    MondayStartTime = Time.newInstance(9, 0, 0, 0),
    MondayEndTime = Time.newInstance(23, 0, 0, 0),
    TuesdayStartTime = Time.newInstance(9, 0, 0, 0),
    TuesdayEndTime = Time.newInstance(23, 0, 0, 0),
    WednesdayStartTime = Time.newInstance(9, 0, 0, 0),
    WednesdayEndTime = Time.newInstance(23, 0, 0, 0),
    ThursdayStartTime = Time.newInstance(9, 0, 0, 0),
    ThursdayEndTime = Time.newInstance(23, 0, 0, 0),
    FridayStartTime = Time.newInstance(9, 0, 0, 0),
    FridayEndTime = Time.newInstance(23, 0, 0, 0),
    SaturdayStartTime = Time.newInstance(9, 0, 0, 0),
    SaturdayEndTime = Time.newInstance(23, 0, 0, 0),
    SundayStartTime = Time.newInstance(9, 0, 0, 0),
    SundayEndTime = Time.newInstance(23, 0, 0, 0),
    IsActive = true,
    IsDefault = true
);

However, when running the test method it fails with a caused by: System.MathException: null while running the BusinessHours.isWithin() method.

I checked the logs for both the variable assignment values and both records appear to have the same exact values. However, I only run against this exception when using the mocked records.


Variable Assignment values:

mocked record:

{
   "Id":"01m000000000001AAA",
   "MondayStartTime":32400000,
   "MondayEndTime":82800000,
   "TuesdayStartTime":32400000,
   "TuesdayEndTime":82800000,
   "WednesdayStartTime":32400000,
   "WednesdayEndTime":82800000,
   "ThursdayStartTime":32400000,
   "ThursdayEndTime":82800000,
   "FridayStartTime":32400000,
   "FridayEndTime":82800000,
   "SaturdayStartTime":32400000,
   "SaturdayEndTime":82800000,
   "SundayStartTime":32400000,
   "SundayEndTime":82800000
}

non mocked record:

{
   "Id":"01m9E0000000WfXQAU",
   "MondayStartTime":32400000,
   "MondayEndTime":82800000,
   "TuesdayStartTime":32400000,
   "TuesdayEndTime":82800000,
   "WednesdayStartTime":32400000,
   "WednesdayEndTime":82800000,
   "ThursdayStartTime":32400000,
   "ThursdayEndTime":82800000,
   "FridayStartTime":32400000,
   "FridayEndTime":82800000,
   "SaturdayStartTime":32400000,
   "SaturdayEndTime":82800000,
   "SundayStartTime":32400000,
   "SundayEndTime":82800000
}

Does anyone have an idea of what might be causing this issue?
Thanks in advance for the help!

Best Answer

BusinessHours.isWithin uses data from the BusinessHours record stored in the database. Since you mocked the ID, it's not a real record, so when it tries to figure out the holidays, you'll end up with complications. In fact, running this code in execute anonymous has the same error, which proves it's not just a unit test problem.

You'll see that the line of code that throws the error is Class.System.BusinessHours.isWithin: line 30, column 1. This is within the BusinessHours system library class. I guess you could call it a bug in the system library, but the net result is that you cannot create a fake Id for a BusinessHours that will be used in the standard system library. You need a real record to work with.

This is often true whenever an Id is needed in any system library call, when performing DML, or when performing a SOQL query to retrieve records by Id.