3

I recently needed to write a test that checks if an exception gets logged. We have a Logger but it's built with static methods, so it cannot be stubbed. But I didn't want to test the Logger logic, so I decided to re-write it to be instantiable.

Once refactoring was done I tried to mock it with ApexMocks. A surprise awaited - StubAPI cannot mock classes with methods that accept Exceptions as argument (logException(Exception ex) for example).

The error thrown is System.TypeException: @Override specified for non-overriding method: void Logger__sfdc_ApexStub.logException(java.lang.Exception ex)

Code sample:

Logger.cls

public class Logger {
    public void logEception(Exception ex) {
        // logging code
    }
}

Test class:

@isTest
private class Test_Logger {
    @isTest
    private static void testMockability() {
        Logger mockLogger = (Logger) Test.createStub(Logger.class, new Stub());
    }

    private class Stub implements StubProvider {
        public Object handleMethodCall(
            Object stubbedObject,
            String stubbedMethodName,
            System.Type returnType,
            List<System.Type> listOfParamTypes,
            List<String> listOfParamNames,
            List<Object> listOfArgs
        ) {
            return null;
        }
    }
}

1 Answer 1

3

The only solution I found was to make the failing methods private and expose them only as static ones. So I ended up with a class with static methods, which uses a mockable singleton under the hood. The remaining issue is that private methods are actually being called and not stubbed, so I added an isMocked variable to prevent private methods from execution.

The code sample below is simplified to only leave what's important. In the production code, I use ApexMocks lib, but it's irrelevant to the issue.

public class Logger {
    @TestVisible
    private static Boolean isMocked = false;

    public static void logEception(Exception ex) {
        new Logger().logException(ex);
    }

    private void logEception(Exception ex) {
        if (isMocked) {
            return;
        }
        // logging code
    }
}

Test

@isTest
private class Test_Logger {
    @isTest
    private static void testMockability() {
        Logger.isMocked = true;
        Logger mockLogger = (Logger) Test.createStub(Logger.class, new Stub());
    }

    private class Stub implements StubProvider {
        public Object handleMethodCall(
            Object stubbedObject,
            String stubbedMethodName,
            System.Type returnType,
            List<System.Type> listOfParamTypes,
            List<String> listOfParamNames,
            List<Object> listOfArgs
        ) {
            return null;
        }
    }
}

You must log in to answer this question.

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