0

So what I'm tying to do is to unit test one of methods in my service:

 @TestVisible
    private void insertSerialNumbers(List<AST_Material_Document_Item__c> documentItems, Map<Id, Asset> assetMap, fflib_SObjectUnitOfWork uow) {
        for (AST_Material_Document_Item__c item : documentItems) {
            if (item.AST_SerialNumberRequired__c) {
                AST_SerialNumber__c serial = new AST_SerialNumber__c(
                        AST_MaterialDocumentPosition__c = item.Id,
                        Name = assetMap.get(item.AST_RelatedAsset__c).SerialNumber,
                        AST_CreatorFlag__c = true
                );
                uow.registerNew(serial);
            }
        }
    }

with test method as below:

@IsTest
    private static void insertSerialNumbersTest() {
        Id itemId = fflib_IDGenerator.generate(AST_Material_Document_Item__c.SObjectType);
        Id assetId = fflib_IDGenerator.generate(Asset.SObjectType);
        AST_Material_Document_Item__c item = new AST_Material_Document_Item__c(
                Id = itemId,
                AST_SerialNumberRequired__c = true,
                AST_RelatedAsset__c = assetId
        );
        Asset relatedAsset = new Asset(Id = assetId, SerialNumber = 'fakeSerialNumber');
        Map<Id, Asset> assets = new Map<Id, Asset>{assetId => relatedAsset};
        List<AST_Material_Document_Item__c> documentItems = new List<AST_Material_Document_Item__c>{item};
        fflib_ApexMocks mocks = new fflib_ApexMocks();
        fflib_SObjectUnitOfWork mockUow = (fflib_SObjectUnitOfWork) mocks.mock(fflib_SObjectUnitOfWork.class);

        AST_MaterialDocumentService service = AST_MaterialDocumentService.newInstance();
        service.insertSerialNumbers(documentItems, assets, mockUow);

        ((fflib_SObjectUnitOfWork)mocks.verify(mockUow, mocks.times(1).description('Missed serial number insert')))
                .registerNew(fflib_Match.sObjectsWith(
                new List<Map<SObjectField, Object>> {
                        new Map<SObjectField, Object> {
                                AST_SerialNumber__c.AST_MaterialDocumentPosition__c => itemId,
                                AST_SerialNumber__c.Name => relatedAsset.SerialNumber,
                                AST_SerialNumber__c.AST_CreatorFlag__c => true
                        }
                }
        ));
    }

Unfortunately with no success:

fflib_ApexMocks.ApexMocksException: EXPECTED COUNT: 1
ACTUAL COUNT: 0
METHOD: fflib_SObjectUnitOfWork__sfdc_ApexStub.registerNew(List<SObject>)
Missed serial number insert
---
ACTUAL ARGS: ()
---
EXPECTED ARGS: [ordered SObjects with [{"AST_CreatorFlag__c":true,"Name":"fakeSerialNumber","AST_MaterialDocumentPosition__c":"a0s000000000001AAA"}]]

Does anyone of you had such issue with testing method which get unit of work as method input? Is this a reason that this mock verification fails?

1 Answer 1

1

Your first port of call should be this answer. See also this recent answer

Normally, one needs to inject the mock UnitOfWork into the fflib factories as such:

Application.UnitOfWork.setMock(mockUow);

But in your case, this isn't necessary as the code under test is passed the mockUow directly:

service.insertSerialNumbers(documentItems, assets, mockUow);

Your verify is trying to verify a list of sobjects using the matcher fflib_Match.sObjectsWith but your code under test calls registerNew in a loop.

Thus, you need this verify:

((fflib_SObjectUnitOfWork)mocks.verify(mockUow, mocks.times(1).description('Missed serial number insert')))
            .registerNew(fflib_Match.sObjectWith(  // singular
                    new Map<SObjectField, Object> {
                            AST_SerialNumber__c.AST_MaterialDocumentPosition__c => itemId,
                            AST_SerialNumber__c.Name => relatedAsset.SerialNumber,
                            AST_SerialNumber__c.AST_CreatorFlag__c => true
                    }
            
    ));

.. and again for each AST_SerialNumber__c your code under test creates

Summary

When verifying a call to the unitOfWork, you need to use the relevant matcher that corresponds to the code under test. If the call to the unitOfWork method has multiple arguments, you need to use matchers for each argument (for example, matching uow.registerRelationship)

2
  • Ok, I understand second part of your answer. But I have some doubts regarding injecting the mock. How to correctly test a method which takes UOW directly in method's signature rather than load it from Application class via Application.unitofwork.newInstance()?
    – Miłosz
    Oct 21, 2021 at 7:10
  • you are right; i amended the post.
    – cropredy
    Oct 21, 2021 at 15:45

You must log in to answer this question.

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