Category Archives: design patterns

Apex Mocks and Verifying Multiple Custom Type Arguments

Part five of a series. Posts include:

Let’s say you have an AccountsService with method doStuff that has two arguments: an fflib_SObjectUnitOfWork and a custom type Map. CancelRequest looks like this:

public class CancelRequest {
  Id id;
  Date cancelDate;
  String cancelReason;

  public CancelRequest(Id val) {this.id = val;}
  public CancelRequest withCancelDate(Date val) {this.cancelDate = val; return this;}
  public CancelRequest withCancelReason(String val) {this.cancelReason = val; return this;}
  public Date getCancelDate() {return this.cancelDate;}
  public String getCancelReason() {return this.cancelReason;}  
}

Now, let’s say you have some code that calls the AccountsService.cancel (this could be a Visualforce controller, invocable method, domain class method, batchable execute(), Apex REST class, etc. – for purposes of this example, it doesn’t matter).

public MyClass {
  public void doStuff(Set<Id> accountIds, String cancelReason) {
    fflib_ISobjectUnitOfWork uow = Application.UnitOfWork.newInstance();  
    Map<Id,CancelRequest> cancelRequests = new Map<Id,CancelRequest>();
    for (Id accountId: accountIds) {
      cancelRequests.put(accountId,new CancelRequest(accountId)
                                    .withCancelDate(Date.today())
                                    .withCancelReason(cancelReason) );
    }
    AccountsService.cancel(uow,cancelRequests);  // cancel accounts w/ date+reason
    uow.commitWork();
  }
}

To unit test MyClass.doStuff(..), you want to mock the AccountsService as all you’re really interested in is that it is called once and with the proper arguments. You have a separate unit test for the actual AccountsService.cancel that checks for the proper DML.

So, let’s build the testmethod…

@isTest
private static void givenAccountIdsAndReasonVerifyDelegationToAccountsServiceCancel() {
   fflib_ApexMocks mocks = new fflib_ApexMocks(); // framework
   
   // Given some accountIds
   Id[] mockAccountIds = new List<Id> {
          fflib_IdGenerator.generate(Account.SObjectType),
          fflib_IdGenerator.generate(Account.SObjectType)
  };

  // Given a cancel reason
  String cancelReason = 'foo';

  // Given a mockAccountsService (assumes standard naming conventions for service implementations
  AccountsServiceImpl mockAccountsService = 
      (AccountsServiceImpl) mocks.mock(AccountsServiceImpl.class);
  Application.Service.setMock(IAccountsService.class,mockAccountsService);

  // Given a mock Unit of Work
  fflib_SObjectUnitOfWork mockUow = 
    (fflib_SObjectUnitOfWork) mocks.mock(fflib_SObjectUnitOfWork.class);
  Application.UnitOfWork.setMock(mockUow);  

  // When doStuff called
  Test.startTest();
  new MyClass().doStuff(new Set<Id> (mockAccountIds),cancelReason);
  Test.stoptest();

  // Then verify service was called only once
  ((AccountsServiceImpl) mocks.verify(mockAccountsService,mocks.times(1)
                            .description('AccountsService.cancel sb called')))
           .cancel((fflib_ISobjectUnitOfWork) fflib_Match.anyObject(),
                   (Map<Id,CancelRequest>) fflib_Match.anyObject()
                  );
  //  Then verify that the service was called with the expected arguments.
  
  // Because the arguments are Apex custom types (and don't implement
  // an equals() and hashcode() method), there is no way for the ApexMocks
  // matchers to verify on equality. So, we fallback to argumentcaptors

  // Set up the captors, one per arg to method AssetsService.cancel
  fflib_ArgumentCaptor capturedUowArg = // arg0
    fflib_ArgumentCaptor.forClass(fflib_ISObjectUnitofWork.class);
  fflib_ArgumentCaptor capturedCancelRequestArg = // arg1
    fflib_ArgumentCaptor.forClass(Map<Id,CancelRequest>.class);

  // Capture the actual args used when the mock service was called
  ((AccountsServiceImpl) mocks.verify(mockAccountsService,1))
    .cancel((fflib_ISobjectUnitOfWork)capturedUowArg.capture(),
            (Map<Id,CancelRequest>)capturedCancelRequestArg.capture()
           );

  // Transform the capturedArgs (represented by type fflib_ArgumentCaptor)
  // into something we can inspect (using getValue() )
  fflib_ISobjectUnitOfWork actualUowArg =
    (fflib_ISobjectUnitOfWork) capturedUowArg.getValue();
 
  Map<Id,CancelRequest> actualCancelRequestArg =
    (Map<Id,CancelRequest>) capturedCancelRequestArg.getValue();
 
  // Now, whew, finally verify values
  System.assertEquals(mockAccountIds.size(),
          actualCancelRequestArg.size(),'all accts sb requested for cancel');

  for (Id accountId: actualCancelRequestArg.keySet() ) {
    System.assertEquals('Foo',
                        actualCancelRequestArg.get(accountId).getCancelReason());
    System.assertEquals(Date.today(),
                        actualCancelRequestArg.get(accountId).getCancelDate());
  }
}

ApexMocks, Answers and void no Argument Domain methods

Part four of a series. Posts include:

Let’s say you have an Opportunity domain method called applyDiscounts(). I’ve seen two approaches to coding (and invoking) this method:

Purist Approach

public void applyDiscounts() {
  for (Opportunity o : (Opportunity[]) Records) {
    .. do work, modifying Records ..
  }
}

Invoke this by:

Opportunities.newInstance(myOppos).applyDiscounts();

Easier To Mock Approach

public void applyDiscounts(Opportunity oppos) {
  for (Opportunity o : oppos) {
    .. do work, modifying calling argument oppos
  }
}

Invoke this by:

Opportunities.newInstance().applyDiscounts(myOppos);

I don’t really like the second approach because it perverts the intention of the Domain class which is to operate on a collection of SObjects provided through its constructor and available in super class variable Records. The Andrew Fawcett book on Enterprise patterns illustrates a custom domain method using the first approach on page 184 (second edition).

If you are using the second approach, you can mock the results of the void method applyDiscounts(oppos) using fflib_Answer. You use Answers when the mocked method returns modified values through its arguments. Enzo Denti has an excellent blog post on how to do this and I won’t bother to repeat this.

But let’s assume you are using the purist approach and need to mock the results of a void domain class method that modifies values passed to the domain class’s constructor. How would you do that?

Assume you have a class DoCoolOpportunityStuff that among other things, has a dependency on the Opportunities domain class and specifically the domain class’s applyDiscounts() method. Let’s set this up:

DoCoolOpportunityStuff (could easily be a service layer class)

public with sharing class DoCoolOpportunityStuff {
  public  void doApplyDiscounts(Set<Id> oppoIds, fflib_ISObjectUnitOfWork uow) {
    // Dependency 1 - Opportunity SObjects
    Opportunity[] oppos = OpportunitiesSelector.newInstance().selectById(oppoIds);

    //	Dependency 2 - Opportunity Domain
    Opportunities.newInstance(oppos).applyDiscounts();
    for (Opportunity o: oppos) {
      if (o.Amount < 0.00) {
	Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
	  email.setSubject(o.Name + ' discounted more than 100%');
	  uow.registerEmail(email);
      }
      uow.registerDirty(o);
     }
   }
}

The above method doApplyDiscounts fetches Opportunities based on a set of ids (Dependency 1) and then instantiates a domain class (Dependency 2) to calculate the discounts on the Opportunities. If the applied discount is more than 100%, it sends an email telling someone of the overly-zealous discount. Everything is done through the UnitOfWork layer so results can be easily tested using ApexMocks.

So, to test this with mocks, we need to mock the Selector (to return mock Oppos) but for good unit tests, we need to also mock applyDiscounts to return changed Opportunities some with Amounts greater than 0.00 and some with Anounts less than 0.00.

Here’s what we need to do in the test method:

static void givenOpportunitiesVerifyApplyDiscounts() {

  Opportunity[] mockOppos = new List<Opportunity> {
    new Opportunity(Id=fflib_IDGenerator.generate(Opportunity.SObjectType),
		Amount=10.0),
    new Opportunity(Id=fflib_IDGenerator.generate(Opportunity.SObjectType),
		Amount=20.0)
  };
  Set<Id> mockOppoIds = new Map<Id,Opportunity>(mockOppos).keySet();

  fflib_ApexMocks mocks = new fflib_ApexMocks();

  //  Given mocks for each of the dependencies
  OpportunitiesSelector mockOpportunitiesSelector = (OpportunitiesSelector) mocks.mock(OpportunitiesSelector.class);
  Opportunities mockOpportunitiesDomain = (Opportunities) mocks.mock(Opportunities.class);
  fflib_SObjectUnitOfWork mockUow = 
     (fflib_SObjectUnitOfWork) mocks.mock(fflib_SObjectUnitOfWork.class);

  mocks.startStubbing();
  mocks.when(mockOpportunitiesSelector.SObjectType())
        .thenReturn(Opportunity.SObjectType);
  mocks.when(mockOpportunitiesSelector.selectById(mockOppoIds))
        .thenReturn(mockOppos);

  mocks.when(mockOpportunitiesDomain.sObjectType())
        .thenReturn(Opportunity.SObjectType);
  ((IOpportunities) mocks.doAnswer(
		new MyApplyDiscountAnswer(mockOppos),mockOpportunitiesDomain))
		.applyDiscounts();

  mocks.stopStubbing();

  // Given mocks injected
  Application.Selector.setMock(mockOpportunitiesSelector);
  Application.Domain.setMock(mockOpportunitiesDomain);
  Application.UnitOfWork.setMock(mockUow);


  //	when service invoked
  new DoCoolOpportunityStuff().doApplyDiscounts(mockOppoIds,mockUow);

  //	then verify oppos domain applyDiscounts called
  ((Opportunities)mocks.verify(mockOpportunitiesDomain,mocks.times(1)
		.description('domain applyDiscounts sb called once')))
	.applyDiscounts();

  //	then verify all mocked oppos registered dirty
  ((fflib_SObjectUnitOfWork)mocks.verify(mockUow,mocks.times(2)
		.description('registerDirty sb called')))
	.registerDirty(fflib_Match.sObjectOfType(Opportunity.SObjectType));
  
  //	then verify mocked Oppo[1] - mocked to discount more than 100%
  //	was noted in an email
  ((fflib_SObjectUnitOfWork)mocks.verify(mockUow,mocks.times(1)
		.description('send email for oppo[1]')))
	.registerEmail((Messaging.Email)fflib_Match.anyObject());

  //	then verify each Oppo (discounted) was dirtied with discount
  ((fflib_SObjectUnitOfWork)mocks.verify(mockUow,mocks.times(1)
		.description('Oppo[0] should have discount')))
	.registerDirty(fflib_Match.sObjectWith(
		new Map<SObjectField,Object> {
		  Opportunity.Id => mockOppos[0].Id,
		  Opportunity.Amount => 0.50 * 10.00
		}
  ));
  ((fflib_SObjectUnitOfWork)mocks.verify(mockUow,mocks.times(1)
	        .description('Oppo[1] should have > 100% discount')))
	.registerDirty(fflib_Match.sObjectWith(
		new Map<SObjectField,Object> {
		  Opportunity.Id => mockOppos[1].Id,
		  Opportunity.Amount => -10.00
		}
  ));
}

Let’s focus on the mock answer for the applyDiscounts() method. Remember, it returns a value through the domain class’s Records variable (the opportunities provided to the domain class’s constructor. The other mocks for the selector and unit of work are standard issue mocks as described in earlier blog posts.

((IOpportunities) mocks.doAnswer(
        new MyApplyDiscountAnswer(mockOppos),mockOpportunitiesDomain))
        .applyDiscounts();

What do we have? We are telling ApexMocks that when the applyDiscounts method is called, answer with the side effects of class MyApplyDiscountAnswer.answer() using the mocked Opportunities (the ones the mocked selector returns). The class MyApplyDiscountAnswer looks like this:

class MyApplyDiscountAnswer implements fflib_Answer {

private Opportunity[] oppos;
  private MyApplyDiscountAnswer(Opportunity[] oppos) {
	this.oppos = oppos;
  }
  public Object answer(fflib_InvocationOnMock invocation) {
    for (Integer i = 0; i < this.oppos.size(); i++) {
	Opportunity o = this.oppos[i];
	  o.Amount = i ==1
	   ? -10.00		// oppo 1 mocked to answer with negative amount
	   : 0.50* o.Amount;
    }
    return null;	// answer must return something
  }
}

You have to implement the answer method. Because we want to return some Opportunities with more than 100% discount applied, we need to dependency inject the Answer with the opportunities returned by the mocked selector. Meta-dependency-injecting! Fun!

So, what happens when the testmethod “when” executes?

new DoCoolOpportunityStuff().doApplyDiscounts(mockOppoIds,mockUow);
  • The code under test starts
  • The code under test fetches Opportunities using the supplied IDs
  • Since we mocked the selector, the code under test fetches our testmethod’s mockOppos
  • These mockOppos serve as the input to the Opportunities domain newInstance(..) method
  • Since we mocked the domain class too, when the applyDiscounts method is requested; ApexMocks uses the custom Answer MyApplyDiscountAnswer to take the mockOppos and modify them with amounts less than and greater than 0
  • The code under test decides that some of the Oppos require an email (because we answered with Amount values less than 0.00). These emails are registered to the unit of work.
  • The code under test finally takes each fetched Opportunity and registers it as dirty (with the discounted amount)

So, the rest of the testmethod simply verifies that the expected emails were registered to the unit of work and the expected sobjects were registered dirty. As with all comprehensive ApexMocks examples – no DML was required to be set up and no DML was executed making the test method lightning fast.

Now it must be admitted that this answer technique only works if you know outside of the code under test what objects will be provided to the domain class’s newInstance(..) method. It works in our example because we’re mocking the output of the selector as the input to the domain construction. And, the output of the selector is under the control of the testmethod .thenReturn(mockOppos) method n the mock stubbing section.

Another example – objects passed to domain constructor not knowable by testmethod

Let’s suppose we have a domain method that derives the ownerId for a collection of Opportunities. Further assume that this void assignOwners() method is really complicated and relies on a massive scaffolding of custom metadata and reference sobjects. Way too much to conveniently prebuild in DML for the testmethod

Further assume we have some service class method that exploits the domain class but the Opportunities passed to the domain constructor are not know outside of the service method:

public  void doAssignOwners1(fflib_ISObjectUnitOfWork uow) {

  Opportunity[] oppos = new List<Opportunity>();

  // Contrived .. construct random # of Oppos. Dependency 1
  for (Integer i = 0; i < Math.mod(System.currentTimeMillis(),5); i++) {
	oppos.add(new Opportunity(Amount = i*100.00));
  }

  //	Dependency 2 - Opportunity Domain
  Opportunities.newInstance(oppos).assignOwners();
  for (Opportunity o: oppos) {
	uow.registerDirty(o);
  }
}

I contrived the method to randomly generate Opportunities but the point being is that the method doAssignOwners isn’t passed predictable Opportunities.

While you could mock the domain class, you have no way in ApexMocks to mocking the opportunities passed to newInstance() and furthermore, even if you did (via a subclass of your Opportunities domain class), these mocked Opportunities wouldn’t flow to the for loop that registers dirty the opportunities that were randomly generated.

So, what to do?

You need to change the doAssignOwners method to have a mockable way of generating the opportunities for the domain class. Remember in our first example, the service method used a selector to generate the Opportunities for use in the domain class. So, we rewrite the service method to look like this:

public class DoCoolOpportunityStuff {
  private final IOpportunityGenerator oppoGenerator;

  public DoCoolOpportunityStuff() {
    this.oppoGenerator = new OpportunityGenerator();
  }

  @TestVisible private DoCoolOpportunityStuff(IOpportunityGenerator mockOppoGenerator) {
    this.oppoGenerator = mockOppoGenerator;
  }

  public  void doAssignOwners2(fflib_ISObjectUnitOfWork uow) {

    // Contrived .. construct random # of Oppos. Dependency 1
    Opportunity[] oppos = this.oppoGenerator.generate();


    //	Dependency 2 - Opportunity Domain
    Opportunities.newInstance(oppos).assignOwners();
    for (Opportunity o: oppos) {
      uow.registerDirty(o);
    }
  }
}

The Opportunities generated for use in the domain class come from another class and, because it is a top level class, we can mock that as well. Note above that the production code of DoCoolOpportunityStuff instantiates an object variable (using the no arg constructor) with the production version of the OpportunityGenerator (below). But we added in a way for the testmethod to use dependency injection to insert a test (mocked) version of this OpportunityGenerator so we can have predictability of the generated Oppos (and hence know which will be passed to the domain class’s newInstance(..) method).

public  class OpportunityGenerator implements IOpportunityGenerator {
  public Opportunity[] generate() {
    Opportunity[] oppos = new List<Opportunity>();
    for (Integer i = 0; i < Math.mod(System.currentTimeMillis(),5); i++) {
      oppos.add(new Opportunity(Amount = i*100.00));
    }
    return oppos;
  }
}

So, now we can test all this using everything we have learned as shown below:

@IsTest
private static void givenNothingVerifyAssignOwnersV2 () {
  // Given mockOppos
  Opportunity[] mockOppos = new List<Opportunity> {
	new Opportunity(Id=fflib_IDGenerator.generate(Opportunity.SObjectType),
			Amount=10.0),
	new Opportunity(Id=fflib_IDGenerator.generate(Opportunity.SObjectType),
			Amount=20.0)
  };
  Set<Id> mockOppoIds = new Map<Id,Opportunity>(mockOppos).keySet();


  fflib_ApexMocks mocks = new fflib_ApexMocks();

  //	Given mockOpportunityGenerator
  IOpportunityGenerator mockOpportunityGenerator = 
    (IOpportunityGenerator) mocks.mock(OpportunityGenerator.class);

  //  Given mock domain and uow
  Opportunities mockOpportunitiesDomain = (Opportunities) mocks.mock(Opportunities.class);
  fflib_SObjectUnitOfWork mockUow = 
    (fflib_SObjectUnitOfWork) mocks.mock(fflib_SObjectUnitOfWork.class);

  mocks.startStubbing();

  mocks.when(mockOpportunityGenerator.generate()).thenReturn(mockOppos);

  mocks.when(mockOpportunitiesDomain.sObjectType())
    .thenReturn(Opportunity.SObjectType);
  ((IOpportunities) mocks.doAnswer(
	new MyAssignOwnerAnswer(mockOppos),mockOpportunitiesDomain))
	.assignOwners();
  mocks.stopStubbing();

  // Given mocks injected
  Application.Domain.setMock(mockOpportunitiesDomain);
  Application.UnitOfWork.setMock(mockUow);
  DoCoolOpportunityStuff coolStuff = new DoCoolOpportunityStuff(mockOpportunityGenerator);

  // when service method called
  coolStuff.doAssignOwners2(mockUow);	// assigns owners

  //	verify each oppo updated and w/ owner
  ((fflib_SObjectUnitOfWork)mocks.verify(mockUow,mocks.times(2)
         .description('2 recs sb modified')))
	 .registerDirty(fflib_Match.sObjectOfType(Opportunity.SObjectType));

  ((fflib_SObjectUnitOfWork)mocks.verify(mockUow,mocks.times(2)
	.description('each domain oppo should have ownerId as per our mocked Answer')))
	.registerDirty(fflib_Match.sObjectWith(
	  new Map<SObjectField,Object>{
	    Opportunity.OwnerId => UserInfo.getUserId()
		}));
  }

  

These lines set up the mockOpportunityGenerator and then inject it to the code under test

//	Given mockOpportunityGenerator
  IOpportunityGenerator mockOpportunityGenerator = 
    (IOpportunityGenerator) mocks.mock(OpportunityGenerator.class);
 ...
// inject via testVisible constructor
DoCoolOpportunityStuff coolStuff = new DoCoolOpportunityStuff(mockOpportunityGenerator);

And to mock the no arg domain class method assignOwners(), we use the same Answer technique in the first example:

((IOpportunities) mocks.doAnswer(
	new MyAssignOwnerAnswer(mockOppos),mockOpportunitiesDomain))
	.assignOwners();

Here we have a different custom fflib_Answer type (see below). This custom Answer assigns the owners to something predictable like the running user’s ID.

class MyAssignOwnerAnswer implements fflib_Answer {

  private Opportunity[] oppos;
  public MyAssignOwnerAnswer(Opportunity[] oppos) {
	this.oppos = oppos;
  }
  public Object answer(fflib_InvocationOnMock invocation) {
    for (Integer i = 0; i < this.oppos.size(); i++) {
      Opportunity o = this.oppos[i];
	o.OwnerId = UserInfo.getUserId();
      }
    return null;	// answer must return something
  }
}

ApexMocks, Selectors, and Formula Fields

Part three of a series. Posts include:

If you’ve bought into mocking Sobjects for use in Unit Tests, you probably have run into the roadblock wherein formula fields and system audit fields can’t be created in SObjects. For example, these won’t compile:

 Account acct = new Account(LastModifiedDate = System.now(), Name = 'Foo');

 Opportunity oppo = new Opportunity(HasLineItems = true, ...);

An example
So, if you have a class method-under-test that wants to do work on Account Opportunities and the work varies based on the value of Opportunity.FormulaField__c

public void doSomeWork(set<ID> acctIds) {
   Integer count = 0;
   for (Account acct : [select Id, 
                            (select Id, FormulaField__c from Opportunities)
                            from Account where Id IN: acctIds])
     for (Opportunity o : acct.Opportunities)
        if (oppo.FormulaField__c == 'foo') doSomeFooWork();
        else doSomeBarWork();

As a good practitioner of Enterprise Patterns, you convert the code to use a Selector:

public void doSomeWork(set<ID> acctIds) {
   Integer count = 0;
   for (Account acct : AccountsSelector.newInstance()
                            .selectWithOpposById(acctIds))
     for (Opportunity o : acct.Opportunities)
       if (oppo.FormulaField__c == 'foo') doSomeFooWork();
       else doSomeBarWork();

I’m assuming you know how to create selectors and the corresponding entry in Application.cls.

The testmethod using ApexMocks

So, let’s set up a typical ApexMocks testmethod where we mock the AccountsSelector.

@isTest private static void testDoSomeWork() {
  fflib_ApexMocks mocks = new fflib_ApexMocks();

  // Given a mock selector
  AccountsSelector mockAcctsSelector = (AccountsSelector)
      mocks.mock(AccountsSelector.class);

  // Given a mock Selector with stubbed results for FormulaField__c
  mocks.startStubbing();
  mocks.when(mockAcctsSelector.SObjectType)).thenReturn(Account.SObjectType);
  mocks.when(mockAcctsSelector
      .selectWithOpposById((set<ID>)fflib_match.anyObject())
      )
      .thenReturn(mockAcctsWithOppos);
  mocks.stopStubbing();

  // Given injected mocks
  Application.Selector.setMock(mockAcctsSelector);

  // When doSomeWork is invoked
  new MyClass().doSomeWork(new set<ID> {});  // don't care about real AccountIds

  // Then verify (not shown here; 
  // perhaps verify uow.registerNew or uow.registerDirty)

So, the question, is, how do we create mockAcctsWithOppos since we need to have values for Opportunity.FormulaField__c?

There is only one way and that is to create JSON and deserialize into the Account Sobject. I’ve used three ways to do this:

Mocking SObjects with Json – method 1 – hard-coded strings

Account[] mockAcctsWithOppos = 
   (Account[]) Json.deserialize(someJsonString,list<Account>.class);

where someJsonString looks like this (example is a single Account with two Opportunities):

{
  "totalSize" : 2,
  "done" : true,
  "records" : [ {
    "attributes" : {
      "type" : "Account",
      "url" : "/services/data/v41.0/sobjects/Account/0013600001FGf1HAAT"
    },
    "Id" : "0013600001FGf1HAAT",
    "Opportunities" : {
      "totalSize" : 2,
      "done" : true,
      "records" : [ {
        "attributes" : {
          "type" : "Opportunity",
          "url" : "/services/data/v41.0/sobjects/Opportunity/0063600000PPLTPAA5"
        },
        "Id" : "0063600000PPLTPAA5",
        "FormulaField__c" : "foo"
      },
       {
        "attributes" : {
          "type" : "Opportunity",
          "url" : "/services/data/v41.0/sobjects/Opportunity/0063600000PPLTPAA5"
        },
        "Id" : "0063600000PPLTPAA6",
        "FormulaField__c" : "bar"
      }
    ]
    }
  }
]
}

You create this Json by using Workbench to generate a query and paste the results into either an Apex string or stick in a StaticResource.

Mocking SObjects with Json – method 2 – fflib_ApexMocksUtils.makeRelationship

The ApexMocks package includes a utility method that can construct the deserialized Json without you having to create the actual string

// Let's mock two Accounts, one with two Oppos, the other with none
ID[] mockAcctIds = new list<ID>();
ID[] mockOppoIds = new list<ID>();
for (Integer i = 0; i < 2; i++) {
  mockAcctIds.add(fflib_IdGenerator.generate(Account.SObjectType);
  mockOppoIds.add(fflib_IdGenerator.generate(Opportunity.SObjectType);
}

Account[] mockAcctsWithOppos = fflib_ApexMocksUtils.makeRelationship(
   Account.class,
   new list<Account> {
     new Account(Id = mockAcctIds[0], Name = '00Account'),
     new Account(Id = mockAcctIds[1], Name = '01Account')
   },
   Opportunity.Account,  // the relationship field
   new list<list<Opportunity>> {
     new list<Opportunity> { . // Two Oppos for Account[0]
         new Opportunity(Id = mockOppoIds[0], AccountId = mockAcctIds[0], 
                         FormulaField__c = 'foo'), 
         new Opportunity(Id = mockOppoIds[1], AccountId = mockAcctIds[0], 
                         FormulaField__c = 'bar')
     },
     new list<Opportunity>();  // no Oppos for Account[1]
  );

This is nice as it lets you do everything without messy string constants. But the utility is limited to only one child relationship so you can’t use it for mocking Accounts with Cases and Opportunities. I find the list> 4th argument to initially be confusing to construct and get right.

Mocking SObjects with Json – method 3 – sfab_SObjectFabricator

Matt Addy has a nice GitHub package to construct Sobjects that is more descriptive and isn’t limited by the number of children. Here is how to use it:

// Let's mock two Accounts, one with two Oppos, the other with none
ID[] mockAcctIds = new list<ID>();
ID[] mockOppoIds = new list<ID>();
for (Integer i = 0; i < 2; i++) {
  mockAcctIds.add(fflib_IdGenerator.generate(Account.SObjectType);
  mockOppoIds.add(fflib_IdGenerator.generate(Opportunity.SObjectType);
}
Account[] mockAcctsWithOppos = new list<Account> {
  (Account) new sfab_FabricatedSObject(Account.class) [0] has 2 Oppos
      .setField(Account.Id = mockAcctIds[0]).
      .setField(Account.Name = '00Account')
      .setChildren('Opportunities', new List<sfab_FabricatedSObject> {
        new sfab_FabricatedSObject(Opportunity.class)
          .setField(Opportunity.Id, mockOppoIds[0]),
          .setField(Opportunity.AccountId, mockAcctIds[0]),
          .setField(Opportunity.FormulaField__c, 'foo'), 
        new sfab_FabricatedSObject(Opportunity.class)
          .setField(Opportunity.Id, mockOppoIds[1]),
          .setField(Opportunity.AccountId, mockAcctIds[0]),
          .setField(Opportunity.FormulaField__c, 'bar')
    }).toSObject(),
  (Account) new sfab_FabricatedSObject(Account.class) //[1] has no Oppos
      .setField(Account.Id = mockAcctIds[1])
      .setField(Account.Name = '01Account')
      .toSObject()
  };

I like the sfab_SObjectFabricator approach as it is clear where you are defining children (and you can also do parents with setParent()).

Some final remarks

  1. Don’t let the inability to construct SObjects with formula or audit fields get in your way to using ApexMocks to mock either inputs to services or domain layers or mock results from services or domain layers. Choose one of the approaches above or roll your own to exploit Salesforce’s feature of constructing any Sobject’s fields via Json deserialization.
  2. The examples above are probably too verbose for the code-under-test. The Opportunity.AccountId, if never referenced, need not be mocked.
  3. Exploit the Unit of Work layer so you can use ApexMocks to verify that your DML (via registerXXX methods) is done as expected – without having to pay for the cost of real DML.
  4. You will still need what I call ‘end-to-end’ testing that doesn’t use mocks to verify that your selectors work against real data and return all the columns the code expects. You also need to verify that actual DML doesn’t run afoul of Validation Rules that otherwise aren’t executed when you mock the Unit Of Work.
  5. ApexMocks are a great way to explore in detail the unit test use cases by focusing the testing problem on the inputs and outputs of a given class/method.