[SalesForce] ApexMocks from FFlib not getting verified at run time

Trying to test a simple overloaded service.

AccountsService.cls

public static List<Account> getAccounts(Set<Id> ids){
    /* This selector needs to be mocked */
    List<Account> accounts = AccountsSelector.newInstance().selectById( ids );

    if(accounts != null && accounts.size() > 0) {
        return accounts;
    } else {
        return NULL;
    }
}

    public static List<Account> getAccounts(Set<String> ids){
        return getAccounts( (Set<Id>)JSON.deserialize(JSON.serialize(ids), Set<Id>.class) );
    }

My thought here is that I simply need to unit test the Set<String> method which will reference the derived method, which then will call the AccountsSelector which should be stubbed per my test.

AccountsServiceTest.cls

@IsTest
private static void getAccounts() {
    /* Create mocks */
    fflib_ApexMocks MOCKS = new fflib_ApexMocks();
    AccountsSelector mockSelector = (AccountsSelector) MOCKS.mock(AccountsSelector.class);

    /* Generate a generic account id */
    Id aid = fflib_IDGenerator.generate(Account.SObjectType);

    //! Given
    MOCKS.startStubbing();
    List<Account> accountsList = new List<Account> {
        new Account(
            Id = aid,
            Name = 'Test Account'
        ) 
    };
    MOCKS.when(mockSelector.selectById(aid)).thenReturn(accountsList);
    MOCKS.stopStubbing();

    /* Configure the application to use mocks */
    Application.Selector.setMock(mockSelector);

    //! When
    AccountsService.getAccounts(new Set<Id>{aid});

    //! Then
    /* Verify services were called */
    ((AccountsSelector) MOCKS.verify(mockSelector)).newInstance().selectById(aid);

}

Unfortunately the test fails with the following response:

fflib_ApexMocks.ApexMocksException: Expected : 1, Actual: 0 -- Wanted but not invoked: AccountsSelector__sfdc_ApexStub.selectById(Id).

It seems the verify method is failing but I don't understand why, I feel like I'm missing something simple but after hours I'm not seeing it. I attempted to test against the method directly as well to make sure there wasn't an issue with the mocking and overloaded methods. I've also replaced the AccountsSelector with the interface IAccountsSelector but that doesn't seem to change anything either.

EDITED:

In addition when I remove the verify line the test passes without an issue and as you can see the AccountsSelector call was covered, so now I'm even more confused :/

enter image description here

Best Answer

This one is a common mistake; the fflib Selector layer requires that you stub the SObjectType() method as the setMock() method used by Application.Selector.setMock(mockSelector) calls SObjectType() to properly instantiate an internal map of selector instances by SObjectTypes (so some selectors can be mocked and others not).

I annotated your code below with the new line...

Now, some other things ...

You are testing getAccounts so you need to verify that it returns the accounts returned by the (mocked) selector. So, your verify is not what you want

You have:

((AccountsSelector) MOCKS.verify(mockSelector)).newInstance().selectById(aid);

You need to verify the return from this method ...I altered your code below. There's no real need to verify that the selector was called because if the code under test doesn't pass in a set of mocked accountIds exactly the same as in your mocks.when, the selector will not return any Accounts and the code under test will not return any Accounts (will return NULL per the way you coded it)

@IsTest
private static void getAccounts() {
    /* Create mocks */
    fflib_ApexMocks MOCKS = new fflib_ApexMocks();
    AccountsSelector mockSelector = (AccountsSelector) MOCKS.mock(AccountsSelector.class);

    /* Generate a generic account id */
    Id aid = fflib_IDGenerator.generate(Account.SObjectType);

    //! Given
    MOCKS.startStubbing();
    List<Account> accountsList = new List<Account> {
        new Account(
            Id = aid,
            Name = 'Test Account'
        ) 
    };
    MOCKS.when(mockSelector.SObjectType()).thenReturn(Account.SObjectType); // REQUIRED
    MOCKS.when(mockSelector.selectById(new Set<Id>{ aid }))).thenReturn(accountsList);
    MOCKS.stopStubbing();

    /* Configure the application to use mocks */
    Application.Selector.setMock(mockSelector);

    //! When
    Account[] results = AccountsService.getAccounts(new Set<Id>{ aid })); // CHG

    //! Then
    /* Verify result was as expected */
    System.assertEquals(1,results.size(),'sb return of mocked accounts');

} 
Related Topic