To help getting past our permission checks during unit tests to test actual functionality, I created a mocking class so people could just call the mocking method they needed from that class for the security class. This includes methods that would take only a "desired result boolean", and uses matchers for the object and field.
I'm now getting errors as if the system doesn't recognize the mocking.
How do I mock this properly between the overloaded signatures and matchers?
I'll detail as best I can, but please let me know if I can supply any additional information.
We added a parameter to the field level methods in the security class to skip redundant object checks, and (to retain the references to the original method signature) overloaded it so that there was the original signature
//Old Security Class method:
isReadable(Boolean desiredResult, SObjectType targetObjectType, String fieldString)
and that one just forwarded to the next with objectCheck = true,
//New Security Class method:
isReadable(Boolean desiredResult, SObjectType targetObjectType, String fieldString, Boolean objectCheck)
isReadable(true, Device.getSObjectType(), 'Courier', true)
This meant we had to update the mocking to handle both method signatures, and a result of true or false for the new one:
//Non matcher and 2 matcher variants of Mocking Class methods
@TestVisible
private static SecurityInterface isReadable(Boolean mockValue, SObjectType targetType, String fieldName){
return isReadable(mockValue, targetType, fieldName);
}
@TestVisible
private static SecurityInterface isReadable(Boolean mockValue, SObjectType targetType){
return isReadable(mockValue, targetType, fflib_Match.anyString());
}
@TestVisible
private static SecurityInterface isReadable(Boolean mockValue){
return isReadable(mockValue, fflib_Match.anySObjectType(), fflib_Match.anyString());
}
//Old mock implementation
@TestVisible
private static SecurityInterface mockSecurityClassIsReadable(Boolean mockValue, SObjectType targetType, String fieldName){
setupSecurityService();
MOCKS.startStubbing();
MOCKS.when(MOCKED_SECURITY_CLASS.isReadable(targetType, fieldName)).thenReturn(mockValue);
MOCKS.stopStubbing();
return MOCKED_SECURITY_CLASS;
}
//New mock implementation
@TestVisible
private static SecurityInterface mockSecurityClassIsReadable(Boolean mockValue, SObjectType targetType, String fieldName){
setupSecurityService();
MOCKS.startStubbing();
MOCKS.when(MOCKED_SECURITY_CLASS.isReadable(targetType, fieldName)).thenReturn(mockValue);
MOCKS.when(MOCKED_SECURITY_CLASS.isReadable(targetType, fieldName, true)).thenReturn(mockValue);
MOCKS.when(MOCKED_SECURITY_CLASS.isReadable(targetType, fieldName, false)).thenReturn(mockValue);
MOCKS.stopStubbing();
return MOCKED_SECURITY_CLASS;
}
This also required that I update the CommonMocks file to include both versions of the Security Class Method:
public Boolean isReadable(SObjectType objectType, String field)
{
return (Boolean) mocks.mockNonVoidMethod(this, 'isReadable', new List<Type> {System.Type.forName('SObjectType'), System.Type.forName('String')}, new List<Object> {objectType, field});
}
public Boolean isReadable(SObjectType objectType, String field, Boolean objectCheck)
{
return (Boolean) mocks.mockNonVoidMethod(this, 'isReadable', new List<Type> {System.Type.forName('SObjectType'), System.Type.forName('String'), System.Type.forName('Boolean')}, new List<Object> {objectType, field, objectCheck});
}
But when I implement my tests, I'm getting errors:
1) Succeeds
2) "CMPL123.fflib_ApexMocks.ApexMocksException: The number of matchers defined (1). does not match the number expected (2)
If you are using matchers all arguments must be passed in as matchers.
For example myList.add(fflib_Match.anyInteger(), 'String') should be defined as myList.add(fflib_Match.anyInteger(), fflib_Match.eq('String'))."
3) "System.NullPointerException: Attempt to de-reference a null object"
The de-reference null object message is typical for when the mocked instance isn't found for the implementation.
@IsTest
private static void mockSecurityClassIsReadableDoesNotThrowsExceptionWhenTrue(){
MockingClass.mockSecurityClassIsObjectReadable(true);
MockingClass.mockSecurityClassIsReadable(true,TEST_OBJECT_TYPE,TEST_FIELD);
Boolean result = false;
Test.startTest();
result = SecurityClass.IsReadable(TEST_OBJECT_TYPE, TEST_FIELD, TEST_OBJECT_MESSAGE, TEST_FIELD_MESSAGE);
Test.stopTest();
System.assertEquals(true,result,'SecurityClass did not return correct result.');
}
@IsTest
private static void mockSecurityClassIsReadableDoesNotThrowsExceptionWhenTrueUsingMatcherFormForObject(){
MockingClass.mockSecurityClassIsObjectReadable(true);
MockingClass.mockSecurityClassIsReadable(true,TEST_OBJECT_TYPE);
Boolean result = false;
Test.startTest();
result = SecurityClass.IsReadable(TEST_OBJECT_TYPE, TEST_FIELD, TEST_OBJECT_MESSAGE, TEST_FIELD_MESSAGE);
Test.stopTest();
System.assertEquals(true,result,'SecurityClass did not return correct result.');
}
@IsTest
private static void mockSecurityClassIsReadableDoesNotThrowsExceptionWhenTrueUsingMatcherFormForObjectAndField(){
MockingClass.mockSecurityClassIsObjectReadable(true);
MockingClass.mockSecurityClassIsReadable(true);
Boolean result = false;
Test.startTest();
result = SecurityClass.IsReadable(TEST_OBJECT_TYPE, TEST_FIELD, TEST_OBJECT_MESSAGE, TEST_FIELD_MESSAGE);
Test.stopTest();
System.assertEquals(true,result,'SecurityClass did not return correct result.');
}