Closed gdoenlen closed 4 years ago
This is a known issue and you're right that's its down to the sobject implementation for equals(..). An sobject is only equal to another if every field value is equal.
The solution is to use matches for stubbing and verification. Try fflib_match.sobjectWithId(..) or fflib_match.refEq(..).
On Tue, 16 Oct 2018, 17:49 George Doenlen, notifications@github.com wrote:
Non-FF Sender
I suspect this mainly has to do with SFDC's implementation of equals for the List class as that is what the class relies on for it's equals (argValues == argValues). I believe is boils down to the list equals method not working correctly when comparing objects instantiated with SObjectType.newSObject and their regular constructors.
I have a class that instantiates a List
and populates its contents via SObjectType.newSObject(Id) which then gets passed to a Repository class which I use to update the records, and this class is what I mock and verify against that the right values were passed to it to update. If I do not verify a list with contents instantiated via newSObject equals will fail even though the values are equivalent. I've provided my test below to show how I'm verifying. Using the commented functionality before the verify call will cause an ApexMocksException to be thrown because ff_lib_MethodArgValues.equals(Object) returns false even though equivalent values will be passed to it. I've verified this via debug logs:
12:25:18.1 (143519714)|USER_DEBUG|[84]|DEBUG|in count calls 12:25:18.1 (143608102)|USER_DEBUG|[85]|DEBUG|fflib_MethodArgValues:[argValues=((Account:{Id=001000000000001AAA, Number_of_Child_Accountsc=200}))] 12:25:18.1 (143660625)|USER_DEBUG|[86]|DEBUG|((Account:{Id=001000000000001AAA, Number_of_Child_Accountsc=200})) 12:25:18.1 (143827303)|USER_DEBUG|[90]|DEBUG|in arg values 12:25:18.1 (143880279)|USER_DEBUG|[91]|DEBUG|fflib_MethodArgValues:[argValues=((Account:{Id=001000000000001AAA, Number_of_Child_Accountsc=200}))] 12:25:18.1 (143926744)|USER_DEBUG|[92]|DEBUG|((Account:{Id=001000000000001AAA, Number_of_Child_Accountsc=200})) 12:25:18.1 (144008510)|USER_DEBUG|[48]|DEBUG|in other 12:25:18.1 (144040162)|USER_DEBUG|[56]|DEBUG|comparing arg values 12:25:18.1 (144074134)|USER_DEBUG|[57]|DEBUG|((Account:{Id=001000000000001AAA, Number_of_Child_Accountsc=200})) 12:25:18.1 (144102713)|USER_DEBUG|[58]|DEBUG|((Account:{Id=001000000000001AAA, Number_of_Child_Accountsc=200})) 12:25:18.1 (144118216)|USER_DEBUG|[59]|DEBUG|using equals 12:25:18.1 (144314303)|USER_DEBUG|[60]|DEBUG|false 12:25:18.1 (146248321)|EXCEPTION_THROWN|[81]|fflib_ApexMocks.ApexMocksException: Expected : 1, Actual: 0 -- Wanted but not invoked: Repository__sfdc_ApexStub.upd(List
). @IsTest static void childAccountCountShouldBeRolledIntoParentAccount() { fflib_ApexMocks mocks = new fflib_ApexMocks(); Repository repo = (Repository) mocks.mock(Repository.class); RollupSelector sel = (RollupSelector) mocks.mock(RollupSelector.class); Id parentId = fflib_IDGenerator.generate(Account.SObjectType);
Map<Id, Account> trgNewMap = new Map<Id, Account>(); for (Integer i = 0; i < 200; i++) { Id accountId = fflib_IDGenerator.generate(Account.SObjectType); trgNewMap.put(accountId, new Account(Id = accountId, ParentId = parentId)); } mocks.startStubbing(); mocks.when(sel.findByLookupIdIn(new Set<Id>{ parentId })) .thenReturn(trgNewMap.values()); mocks.stopStubbing(); Test.startTest(); AccountAfterExec exec = new AccountAfterExec(repo, sel, trgNewMap, null); exec.execute(); Test.stopTest(); /* * We have to instantiate the verify list the same way * we do in the Rollup class as there seems to be a bug * in the equals method when instantiating with `newSobject` * and doing it regularly. */ List<SObject> ret = new List<SObject>(); SObject acc = Account.SObjectType.newSObject(parentId); acc.put(Account.Number_of_Child_Accounts__c.getDescribe().getName(), 200); ret.add(acc); /* THIS DOES NOT WORK List<SObject> ret = new List<SObject>{ new Account( Id = parentId, Number_of_Child_Accounts__c = 200 )}; */ ((Repository) mocks.verify(repo)).upd(ret); }
Please let me know if I have no been clear enough or if you require more details.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/financialforcedev/fflib-apex-mocks/issues/67, or mute the thread https://github.com/notifications/unsubscribe-auth/ANciZ_JI4WJerRWsNxo3vfkqOiQHNalFks5ulg4JgaJpZM4XexQG .
Thanks @gdoenlen for the detailed summary! Also if you want to continue to discuss. 👍
I suspect this mainly has to do with SFDC's implementation of equals for the List class as that is what the class relies on for it's equals (argValues == argValues). I believe is boils down to the list equals method not working correctly when comparing objects instantiated with
SObjectType.newSObject
and their regular constructors.I have a class that instantiates a
List<SObject>
and populates its contents viaSObjectType.newSObject(Id)
which then gets passed to aRepository
class which I use to update the records, and this class is what I mock and verify against that the right values were passed to it to update. If I do not verify a list with contents instantiated vianewSObject
equals will fail even though the values are equivalent.I've provided my test below to show how I'm verifying. Using the commented functionality before the verify call will cause an
ApexMocksException
to be thrown becauseff_lib_MethodArgValues.equals(Object)
returns false even though equivalent values will be passed to it. I've verified this via debug logs:Please let me know if I have no been clear enough or if you require more details.