Category Archives: testmethod

ApexMocks, Selectors, and Formula Fields

This is part 3 of a series, see Part 1 and Part 2

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.

ApexMocks and Email

Part two of a series. See Part 1 and Part 3

One of the problems in unit testing outbound email is that it is hard to verify that you constructed all of the properties of the outbound email as expected. This is compounded by the fact that sandbox orgs default with email deliverability ‘off’ so any attempt to use Messaging.sendEmail(emails) will throw an exception and your test breaks.

The example’s premise

Suppose we have a simple class method that constructs and sends an email:

public void sendEmail() {
   Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
   mail.setToAddresses(new list<String> {'foo@bar.com'};
   mail.setSubject('Greetings, earthlings!');
   ...
   Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}

How would you unit test that the outbound email was sent to ‘foo@bar.com’ ? Not so simple. Same for the other properties of the outbound email.

Enter Enterprise Patterns and ApexMocks

Rework the Apex class to use the fflib UnitOfWork:

public void sendEmail() {
   fflib_ISobjectOfWork uow = Application.UnitOfWork.newInstance();
   Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
   mail.setToAddresses(new list<String> {'foo@bar.com'};
   mail.setSubject('Greetings, earthlings!');
   ...
   uow.registerEmail(mail); // let UnitOfWork know mail is part of Txn
   uow.commitWork(); // send the mail
}

The fflib_SobjectUnitOfWorkClass considers outbound emails as part of the transaction, hence the registerEmail(mail) method call.

Now, your testmethod looks like this:

@isTest private static void testSuccessPath() {
  fflib_ApexMocks mocks = new fflib_ApexMocks();
  // Given a mock UoW (injected)
  fflib_SobjectUnitOfWork mockUow = 
     (fflib_SobjectUnitOfWork) mocks.mock(fflib_SObjectUnitOfWork.class);
  Application.UnitOfWork.setMock(mockUow);

  // When the email method is invoked
  new MyClass().sendEmail();

  // Then verify that an email was constructed and sent
  ((fflib_SobjectUnitOfWork) mocks.verify(mockUow,
                                          mocks
                                           .times(1)
                                           .description('email sb constructed')))
    .registerEmail((Messaging.SingleEmailMessage) fflib_Match.anyObject());
    									
    									
  ((fflib_SobjectUnitOfWork) mocks.verify(mockUow,
                                          mocks
                                           .times(1)
                                           .description('email sb sent')))
    .commitWork();

  // Then verify that the email was constructed as expected
  // We use ArgumentCaptors for this. There are four (4) steps:

  fflib_ArgumentCaptor capturedEmailArg	= 
        fflib_ArgumentCaptor.forClass(Messaging.SingleEmailMessage.class);
  ((fflib_SobjectUnitOfWork) mocks.verify(mockUow,1))
       .registerEmail((Messaging.SingleEmailMessage)capturedEmailArg.capture());

  Object actualEmailAsObject = capturedEmailArg.getValue();
  Messaging.SingleEmailMessage actualEmail = 
        (Messaging.SingleEmailMessage) actualEmailAsObject;

  System.assertEquals('Greetings, earthlings!', 
                       actualEmail.getSubject(),
                       'subject is from friendly aliens');
  System.assertEquals(new list<String> {'foo@bar.com'},
                       actualEmail.getToAddresses()
                       'only @bar.com domains expected');
  ... other properties.
}

Let’s look at the argumentCaptor, one line at a time.

fflib_ArgumentCaptor capturedEmailArg	= 
        fflib_ArgumentCaptor.forClass(Messaging.SingleEmailMessage.class);

We declare a variable of type fflib_ArgumentCaptor and set it to be of the type we want to inspect.

((fflib_SobjectUnitOfWork) mocks.verify(mockUow,1))
       .registerEmail((Messaging.SingleEmailMessage)capturedEmailArg.capture());

We ask the mocking framework that when the UnitOfWork object is called with method registerEmail with single argument of type Messaging.SingleEmailMessage that we want to capture the value of that argument when the mock UoW is called. The capture() method of ApexMocks library does this. Note we use the mocks.verify(..) method to do this. That is, instead of verifying the value passed to registerEmail we are capturing that value for later inspection.

Object actualEmailAsObject = capturedEmailArg.getValue();
  Messaging.SingleEmailMessage actualEmail = 
        (Messaging.SingleEmailMessage) actualEmailAsObject;

Our declared variable capturedEmailArg has a method getValue() provided by the fflib_ArgumentCaptor class. It returns an Object. There is also a getValues() method for collections. We cast this to the type we care about – Messaging.SingleEmailMessage.

Now, we can assert against the actual email that the class-under-test constructed and verify each property.

System.assertEquals('Greetings, earthlings!', 
                       actualEmail.getSubject(),
                       'subject is from friendly aliens');

So, why is this cool?

  1. We are immune from the sandbox having to be configured to send emails. Because fflib is already unit tested, calls to registerEmail(someEmail) followed by commitWork() will send emails in an org configured to send emails. We merely need to verify that registerWork and commitWork got called. Since the UnitOfWork layer is mocked, we can use ApexMocks to verify that calls to a mockUow are as expected.
  2. The fflib_ArgumentCaptor feature of ApexMocks allows detailed inspection of arguments passed to any mockable class/method. In our example, it is a single argument of type Messaging.SingleEmailMessage, but it could be any arbitrary Apex type. Thus, your unit tests can be quite exhaustive about verifying that the contract between two objects is fulfilled without having to construct any real Sobjects, do any real DML, or query for the results.

Apex Mocks and Enterprise Patterns (First in a Series)

It took me a bit to get here but I can unequivocally state that Apex Mocks is well worth learning to improve your unit testing.

This blog post aims at the Apex developer who doesn’t have Java Mockito experience (which was me). Since most of the code at our org relies on the Apex Enterprise Patterns, the examples herein will exploit that. The most useful reference to me was chapter 11 – Unit Testing of Force.com Enterprise Architecture although there were some typos in the code examples in my printed copy.

See also Part 2 (Unit Testing email), and Part 3 (Selector mocking with formula fields)

The scenario
You have a CasesService, method reopen(set csIds)

Here’s the code: Note use of the Unit of Work Pattern

public virtual class CasesServiceImpl implements ICasesService{

    /**
    *	reopen	- reopen a set of Cases
    **/
    public virtual void reopen(set<ID> csIds) {
        try {
            fflib_ISobjectUnitOfWork uow = Application.UnitOfWork.newInstance();
        	reopen(uow,csIds);
        	uow.commitWork();
        }
        catch (Exception e) {
            throw new CasesService.CaseReopenException(e.getTypeName() + ' ' + e.getMessage() + ' ' + e.getStackTraceString());
        }
    }
    

    public virtual void reopen(fflib_ISobjectUnitOfWork uow, set<ID> csIds) {
        for (ID csId : csIds) {
            uow.registerDirty(new Case(Id = csId, Status = Cases.STATUS_NEW));
        }    
    }
    
}

I’m presuming if you are reading this, that you already know the Apex Enterprise Pattern and have created the ICasesService.cls, CasesService.cls, and updated the Application.cls.

Unit testing the service
In “classic” Apex development, you would test the CasesServiceImpl.reopen(set csIds) method by creating via DML a Case record (this might also require creating an Account and Contact record). Sort of like this:

@isTest private static void testCaseReopen() {
   Account[] mockAccts = new list<Account> {
     new Account(...),
     new Account(...)
     ...
   };
   insert mockAccts;
   // and so on for the Contacts (mockContacts) and Cases (mockCases)

   Test.startTest(); 
   CasesService.reopen(new set<ID> (new map<ID,Case>(mockCases).keySet());
   Test.stopTest();
   Case[] resultCases [select Id, Status from Case];
   for (Integer i = 0; i < resultCases.size(); i++)
      System.assertEquals('New',resultCases[i].Status,
                          '['+i+'] Status should be New');
}

So boring. And so much DML that makes your tests run longer – especially if you have hundreds or thousands of tests. You have to query the database to test your asserts. Think about testing Opportunities – Accounts, Contacts, Opportunities, Pricebooks, Product2s, PricebookEntries, and OpportunityLineItems may all have to be inserted into the database.

And here’s the beauty
Because you are using Apex Enterprise Patterns, you recognize that the reopen method only needs to be passed in Case Ids and then verified that the Cases were registered as dirty and that commitWork was called.

  • You don’t need real Cases to generate valid Case Ids
  • You don’t need to query the updated records to see if the Status was changed. Instead, you just need to know that registerDirty() was called with the expected status of ‘New’. As long as commitWork() was called, you can rely on the updates being done (written to the database) because commitWork() is already unit tested by the fflib library.


So, what does the unit test look like?

I’ll start off by saying that the syntax takes a bit of time getting used to. I’ll try to comment each step

@isTest private static void testReopen() {
  /** variable mocks represents the mocking framework object. 
     You can call it anything **/
  
  fflib_ApexMocks mocks = new fflib_ApexMocks();  // required and first
 
  // Given Case Ids to reopen 
  /** We use fflib to generate valid-for-the-sobjectType IDs **/
  
  ID[] mockCaseIds = new list<ID>();
  for (Integer i = 0; i < 2; i++) // 2 is arbitrary but goal is bulk testing 	
    mockCaseIds.add(fflib_IdGenerator.generate(Case.SobjectType));

  // Given mock UnitOfWork
  fflib_SobjectUnitOfWork mockUow = (fflib_SobjectUnitOfWork) 
                                       mocks.mock(fflib_SObjectUnitOfWork.class);
  Application.UnitOfWork.setMock(mockUow); // inject the mock for fflib factories

  /** Wrap your service call in try catch so you can easily debug any errors **/
  // When service invoked
  try {
   CasesService.reopen(new set<ID>(mockCaseIds)); 
  }
  catch(Exception e) {
   System.assert(false,'snb, service method sb success; ' + showException(e));
  }

  // Then verify Case status is New and objects committed 
  /** mocks.verify takes two arguments:
       1 - the object being mocked - in this case, the UnitOfWork
       2 - an object of type fflib_VerificationMode. 
           This object supports a fluent pattern with methods:
            times(n) - how many times the mock object's method M is called
            atLeast(n) - method called at least n times
            atMost(n) - method called at most n times
            atLeastOnce() - method called at least one time
            between(m,n) - method called between m and n times
            never() - method never called
            calls(n) - method called n times w/ InOrder verifier (see doc)
            description(text) - equivalent to the third argument for System.assertEquals()
  
       Once cast to the type of the mock object, then verification is that the 
       registerDirty method was called (twice) with an Sobject of type Case
  **/         
  ((fflib_SobjectUnitOfWork) 
    mocks.verify(mockUow,mocks
                  .times(2)
                  .description('both Cases should be marked as Closed'))
  )
   .registerDirty(fflib_Match.sObjectOfType(Case.SObjectType));


  /** In this verify, assert registerDirty was called with an SObject matching 
      1 - Case.Id = the first mocked CaseId
      2 - Case.Status is 'New'

     This is done with a matcher (type = fflib_Match) argument. 
     The framework looks to see if the method (registerDirty) 
     was called with an argument that "matches" the matcher.

     There are a large number of matchers, see class
     fflib_Match (most of the Mockito matchers are mirrored). 
     One of the Apex-specific ones is sObjectWith that takes a map of
     SObjectField => Object
  **/

  ((fflib_SobjectUnitOfWork) 
    mocks.verify(mockUow,mocks
                  .times(1)
                  .description('Case[0] sb reopened'))
  )
   .registerDirty(fflib_Match.sObjectWith(new map<SObjectField,Object> {
        Case.ID => mockCaseIds[0],    																			
        Case.Status => Cases.STATUS_NEW}));
		
  /** In this verify, assert the second Case ID also became status New **/
  ((fflib_SobjectUnitOfWork) 
    mocks.verify(mockUow,mocks
                  .times(1)
                  .description('Case[1] sb reopened'))
  )
   .registerDirty(fflib_Match.sObjectWith(new map<SObjectField,Object> {
        Case.ID => mockCaseIds[1],    																			
        Case.Status => Cases.STATUS_NEW}));

  /** Finally, assert records were committed to Database. 
      As commitWork has no args there is no Matcher **/

  ((fflib_SobjectUnitOfWork) 
    mocks.verify(mockUow,mocks
                   .times(1)
                   .description('both Cases should be committed'))
  )
   .commitWork();

}

Some meta remarks

  1. Using ApexMocks requires some wordy syntax that is not immediately apparent when you first see it
  2. It is especially useful for unit testing smallish methods that have few dependencies (which is how they should be written in the first place). Doing unit testing improves your confidence that methods will do what they are supposed to do when orchestrated into larger transactions.
  3. You can mock only non-static methods of objects.
  4. You need to invest time in learning how Matchers work. The class fflib_Match and fflib_ApexMocksTest are helpful. So are Mockito books and the Mockito web site
  5. As we’ll see in subsequent posts, you can mock selectors including formula fields and system audit fields without having to do any DML or SOQL. You can also mock the domain layer and service layer, and for that matter any non-static object method (except async)

The goal of this post was to explain the syntax. Try it out on a small class and you’ll feel you are up’ing your Unit test game, hence leaving a stronger legacy of test code to yourself, your team members, and your successors.

Testing a Batchable + Queueable + Schedulable

It isn’t super clear in the documentation (V36) what happens in a test method when a Batchable, Queueable, and Schedulable are involved within the Test.startTest()...Test.stoptest() execution scope.

The system executes all asynchronous processes started in a test method synchronously after the Test.stopTest statement

So, I decided to do a simple experiment:

The class (acts as both a batchable, queueable, and schedulable)

public with sharing class FooBatchableQueueable 
             implements Database.Batchable<Sobject>, Queueable {
    
    
    public Database.QueryLocator start(Database.BatchableContext bc) {
    	System.debug(LoggingLevel.INFO,'Entered Batchable start()...');
    	return  Database.getQueryLocator([select id from Group 
                                              where DeveloperName = 'Foo']);
    }
    
    public void execute(Database.BatchableContext bc, List<Group> scope) {
    	System.debug(LoggingLevel.INFO,'Entered Batchable execute()...');
    	System.enqueueJob(new FooBatchableQueueable());
    	System.debug(LoggingLevel.INFO,'within Batchable execute(), after enqueuing the job...');
    }
    public void finish(Database.BatchableContext bc) {
    	System.schedule('FooSchedulable','0 0 0 1 1 ?', new FooSchedulable());
    	System.debug(LoggingLevel.INFO,'within Batchable finish(), after scheduling');
    }
    
    public void execute(QueueableContext qc) {
    	System.debug(LoggingLevel.INFO,'reached Queueable execute()');
    }
    

    public class FooSchedulable implements Schedulable {
    	public void execute(SchedulableContext sc) {
    		System.debug(LoggingLevel.INFO,'reached Schedulable execute()');
    	}   	
    }
}

And the testmethod

@isTest
private with sharing class FooBatchableQueueableTest {
    
    @isTest private static void testBatchableQueueable() {
    	insert new Group(DeveloperName='Foo', name='Foo', type='Regular');
    	Test.startTest();
    	Database.executeBatch(new FooBatchableQueueable());
    	Test.stoptest();
    	//	Async batchable should execute, then queueable,  
        //      then schedulable. Or do they? See debug log
    }
}

And, what does happen?

  1. The batchable start() and execute() execute fine.
  2. The execute() calls System.enqueueJob(..)
  3. The Queueable job starts, and its execute() method is invoked. See the debug Log
  4. The batchable finish() method executes. It does a System.schedule() on a new object.
  5. The schedulable’s execute does not start.

Debug log

Entered Batchable start()…
Entered Batchable execute()…
within Batchable execute(), after enqueuing the job…
reached Queueable execute()
Entered Batchable finish()…
within Batchable finish(), after scheduling

Conclusion

  • Both the batchable and the queueable, as async transactions are executed “synchronously” once the Test.stopTest() is reached in the testmethod.
  • You definitely cannot assume that the batch finish() will execute before the queueable execute().
  • The constructor for the schedulable class will get invoked, but not its execute() method. You can see no debug log from within the schedulable’s execute().
  • You will need to explicitly test the schedulable by mocking the environment prior to its scheduling and then invoking in a separate testmethod.

Testing Viewstate in Apex testmethods

If you have a controller plus VF page that could be subject to viewstate issues and you want to avoid introducing viewstate issues once you have already tested some released version of your code, how should you go about doing this?

There is (as of V36.0), no PageReference method getViewStateSize() which would be the obvious choice.

So, is there a way to approximate the viewstate and verify it doesn’t grow as more data volume increases?

First Principles: What is Viewstate comprised of?

Viewstate is used to preserve state across HTTP stateless protocol as the page interacts with the controller via GETs and POSTs (at least until a client-side redirect occurs). From the Salesforce Developer Doc, Viewstate is made up of:

  • All non-transient data members in the associated controller (either standard or custom) and the controller extensions.
  • Objects that are reachable from a non-transient data member in a controller or controller extension.
  • The component tree for that page, which represents the page’s component structure and the associated state, which are the values applied to those components.
  • A small amount of data for Visualforce to do housekeeping.

So, from a testmethod point of view, we have access to the first two: non-transient data members and their reachable objects. The component tree will vary in size depending on how many components are rendered – and this typically varies with the number of list elements in apex:pageBlockTables, apex:dataTables, and apex:repeats. The Visualforce housekeeping space consumption is not available to us.

Steps to take
Assuming we have followed the excellent guidelines in Visualforce in Practice, Chapter 13, Visualforce Performance and Best PracticesRapidfire Rendering of Visualforce Pages, what remains is to use the Apex testmethods as a regression suite to early warn you if you have introduced a viewstate regression, at least from the controller side.

Note, there are ways to avoid Viewstate altogether via client-side techniques such as Javascript Remoting but for purposes of this Blog post, let’s assume you have a traditional VF controller + VF markup.

Here’s a coding approach to use in your testmethod:

@isTest private static void testViewState() {
  // Mock n Sobjects
  MyController ctlr = new Controller();  // same principle applies for extensions
  // exercise setters, getters, and ajax action methods that process all n records 
  Integer ctlrSerializedSize = Json.serialize(ctlr);  // get size of controller, serialized

  // mock another n SObjects so we have 2n total
  ctlr = new Controller();  
  // exercise setters, getters, and ajax action methods that process all 2n records 
  System.assertEquals(ctlrSerializedSize,Json.serialize(ctlr),
                      'non transient size of controller should stay flat');
}

Caveats

  1. For Json.serialize to work, you can’t have any non-transient, unserializable variables like System.SelectOption. Use methods rather than getter properties. A list of restrictions can be found in the Apex Developer Doc – JSON Support. Some things may be hard to work around.
  2. The equality assert given in the code sample most likely works only if the same input conditions are used for the test on n records as is used on 2n records.
  3. I’ll say it again, this won’t test the number of VF apex:components generated by 2n versus n — that could blow up your view state. Hopefully you addressed this the first time with your design by following the aforementioned VF in Practice guidelines, Chapter 13.

Builder pattern – in testmethod asserts

A common problem in business applications is to test multiple field values set by some service in an SObject. Normally, you might start down via this approach:

// ...code that updates some Opportunity

// now verify against expected values
Opportunity oActual = [select amount, closeDate, stagename from Opportunity where ...];
System.assertEquals(1000.0, oActual.amount);
System.assertEquals(Date.newIstance(2020,1,1), oActual.closeDate);
System.assertEquals('Closed Won',oActual.Stagename);

But this has several problems.

  1. It is tedious to type
  2. The testmethod stops on the first error yet other errors may be lurking. This is especially true if you have to verify many SObject fields. So, you end up running a test, finding an error, then fixing, then rerunning, then exposing a new error. Rinse and repeat and most likely, your attention has wandered into social media before too long.

Thanks to my colleagues Adrian Larson, sfdcfox and Keith C on Salesforce Stackexchange, I was introduced to the Builder Pattern. I decided to apply it to this issue.

Objective
I was looking to get all field verify errors for an SObject exposed in a single System.assert. So, the goal was to code this:

System.assertEquals(SObjectVerify.NO_ERRORS,OpportunityVerify.getInstance(oActual)
  .withAmount(60000)
  .withStage('Closed Won')
  .withCloseDate(Date.newINstance(2020,1,1)
  .results());

that, if it doesn’t verify, displays the assertion failure as:

System.AssertException: Assertion Failed: Expected: , Actual:
AccountId Expected: 00540000000wbFSAAY vs. Actual: 0014000000HQGCMAA5
CloseDate Expected: null vs. Actual: 2006-10-14 00:00:00

So, here goes the code – one base (super) class that does all the work and one , each, domain (SObject-specific) class.

The base (super) class

public abstract class SObjectVerify {
	
	//	-------------------------------------
	//	Inner Class to track Variances between 
	//	expected and actual values for a given field
	//	-------------------------------------
	private class Variance {
		Object				actVal;
		Object				expVal;
		
		private Variance(Object expVal, Object actVal) {
			this.expVal		= expVal;
			this.actVal		= actVal;
		}
	}
	private map<Schema.SobjectField,Variance> fldToVarianceMap = new map<Schema.SobjectField,Variance>();
	
	public static final String NO_ERRORS	= '';	// Used by caller in system.assert as expectedResult
	
    private Sobject	actSobj;	// the actual Sobject
    
    // If all expected values match all actuals, 
    //	return NO_ERRORS, otherwise, return a line-broken string of variances
    protected String getResults() {
    	String res 		= NO_ERRORS;
    	for (Schema.SobjectField fld : fldToVarianceMap.keySet())
    		res += '\n   ' + fld + ' Expected: ' + fldToVarianceMap.get(fld).expVal + 
    			     ' vs. Actual: ' + fldToVarianceMap.get(fld).actVal;
    	return res;	
    }

	//	(super) constructor
	public SobjectVerify(SObject actSobj) {this.actSobj = actSobj;}
	
	
	//	Builder pattern, returns ourselves after comparing 
	//	the actualFldVal vs expectedFldVal, stashing variances in a map
	protected SObjectVerify verify(Object expVal, Schema.SObjectField fld) {
		Object actVal	= this.actSobj.get(fld);
		if (expVal == null) {
			if (actVal != expVal)
				this.fldToVarianceMap.put(fld,new Variance(expVal,actVal));
		}
		else
		if (expVal instanceOf Blob) {
			if ((Blob) actVal != (Blob) expVal) 
				this.fldToVarianceMap.put(fld,new Variance(expVal,actVal));
		}
		else
		if (expVal instanceOf Boolean) {
			if ((Boolean) actVal != (Boolean) expVal) 
				this.fldToVarianceMap.put(fld,new Variance(expVal,actVal));
		}
		else
		if (expVal instanceOf Date) {
			if ((Date) actVal != (Date) expVal) 
				this.fldToVarianceMap.put(fld,new Variance(expVal,actVal));
		}
		else
		if (expVal instanceOf DateTime) {
			if ((DateTime) actVal != (DateTime) expVal) 
				this.fldToVarianceMap.put(fld,new Variance(expVal,actVal));
		}
		else
		if (expVal instanceOf Decimal) {
			if ((Decimal) actVal != (Decimal) expVal) 
				this.fldToVarianceMap.put(fld,new Variance(expVal,actVal));
		}
		else
		if (expVal instanceOf ID) {
			if ((ID) actVal != (ID) expVal) 
				this.fldToVarianceMap.put(fld,new Variance(expVal,actVal));
		}
		else
		if (expVal instanceOf Integer) {
			if ((Decimal) actVal != (Integer) expVal) 
				this.fldToVarianceMap.put(fld,new Variance(expVal,actVal));
		}
		else
		if (expVal instanceOf String) {
			if ((String) actVal != (String) expVal) 
				this.fldToVarianceMap.put(fld,new Variance(expVal,actVal));
		}
		return this;												
	}
}

The domain (Sobject-specific) Class

public class OpportunityVerify extends SObjectVerify {
	
	//	Usage
	//	System.assertEquals(SObjectVerify.NO_ERRORS,OpportunityVerify.getInstance(someActualOpportunity)
	//							.withXXX(someExpectedValFldXXX)
	//							.withYYY(someExpectedValFldYYY)
	//							.results();
	
	//	If the assertion fails, System.assert displays for each field at variance (separated by \n):
	//
	//		fldXXX expected: .... vs. actual: ....
	//		fldYYY expected: .... vs. actual: ....
	
	private SObject actSObj;   // actual Opportunity, to be compared with Expected Opportunity
	
	public OpportunityVerify withAccountId(Object expVal) 	{
           return (OpportunityVerify) verify(expVal,Opportunity.AccountId);
        }
	public OpportunityVerify withAmount(Object expVal) 	{
           return (OpportunityVerify) verify(expVal,Opportunity.Amount);
        }
	public OpportunityVerify withCampaignId(Object expVal) 	{
           return (OpportunityVerify) verify(expVal,Opportunity.CampaignId);
        }
	public OpportunityVerify withCloseDate(Object expVal) 	{
           return (OpportunityVerify) verify(expVal,Opportunity.CloseDate);
        }
	public OpportunityVerify withHasOli(Object expVal) 	{
           return (OpportunityVerify) verify(expVal,Opportunity.HasOpportunityLineItem);
        }
	public OpportunityVerify withStage(Object expVal) 	{
           return (OpportunityVerify) verify(expVal,Opportunity.StageName);
        }
	
	public static OpportunityVerify	getInstance(SObject actSobj) {
		return new OpportunityVerify(actSobj);
	}
	
	public String results() {
		return super.getResults(); // super class returns either NO_ERRORS (empty string) or a single string of variances)
	}
	
	public OpportunityVerify(SObject actualSobj) {
		super(actualSobj);
	}
}

Additional advantages

  1. If you need to incrementally add new field verifications, you only need to add a new withXXX method to the theDomainObjectVerify class.

Some possible extensions

  1. Verify a batch of records in a single assert, with the errors indexed by the position in the list

Partial success and trigger firing

Yesterday, I was baffled by the following observed behavior:

  1. Insert six Contacts using Database.insert(cList,false), five of which had some validation error, one which succeeded
  2. The successful Contact, in the before insert trigger, derives the value of accountId, if null in the inserted record
  3. The system.assert to verify the defaulting of accountId failed, even though the debug log clearly showed that it was set

Further investigation of the debug log showed the before insert trigger for the successful Contact was executing twice, and in the second iteration, the defaulting of the accountId was not occurring.

Before insert trigger executed twice? Huh?

After some reductionist testing, I verified that this only happened when using optAllOrNothing = false in Database.insert(cList,false). That is, allow for partial successes. According to the documentation (in a section I had never read or paid attention to).

When errors occur because of a bulk DML call that originates from the SOAP API with default settings, or if the allOrNone parameter of a Database DML method was specified as false, the runtime engine attempts at least a partial save:

(1) During the first attempt, the runtime engine processes all records. Any record that generates an error due to issues such as validation rules or unique index violations is set aside.

(2) If there were errors during the first attempt, the runtime engine makes a second attempt that includes only those records that did not generate errors. All records that didn’t generate an error during the first attempt are processed, and if any record generates an error (perhaps because of race conditions) it is also set aside.

(3) If there were additional errors during the second attempt, the runtime engine makes a third and final attempt which includes only those records that didn’t generate errors during the first and second attempts. If any record generates an error, the entire operation fails with the error message, “Too many batch retries in the presence of Apex triggers and partial failures.”

It is the second point that is interesting – “the runtime engine makes a second attempt that includes only those records that did not generate errors” – ahhh, that is why the debug log showed two executions of before insert on the same record.

Now, why did the second attempt not default the Contact’s accountId using my Apex logic?

Answer: At the end of my before insert trigger handler, I set a static variable to prevent the trigger handler from re-executing (sort of a reflex action to avoid unnecessary SOQLs). Hence, when the second attempt at before insert was made, the static variable prevented the defaulting and the record saved successfully, except without a non-null AccountId. Hence the assertion failed.

A different portion of the SFDC Apex doc states:

When a DML call is made with partial success allowed, more than one attempt can be made to save the successful records if the initial attempt results in errors for some records. For example, an error can occur for a record when a user-validation rule fails. Triggers are fired during the first attempt and are fired again during subsequent attempts. Because these trigger invocations are part of the same transaction, static class variables that are accessed by the trigger aren’t reset. DML calls allow partial success when you set the allOrNone parameter of a Database DML method to false or when you call the SOAP API with default settings. For more details, see Bulk DML Exception Handling.

Note the sentence: “Because these trigger invocations are part of the same transaction, static class variables that are accessed by the trigger aren’t reset”.

So, while governor limits are not affected by the retry, the static variables remain persistent and hence the trigger handler derivation logic remained switched off as a result of the conclusion of the initial before insert trigger handler.

Removing the setting of the static variable solved the issue.

Batchable class – test data – callouts: How to test?

The short hand title for this post might also be System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

Here’s the problem I recently faced:

  1. Batchable class that does callouts in the start() method.
  2. Testmethod needs to setup some test data before the batchable class executes

As you probably know if you are reading this ..

  • Testmethods can’t do callouts, you have to mock out the callout response using Test.setMock(..)
  • Async tasks like Database.executeBatch() or @future methods don’t execute in a test context until the Test.stopTest() method.

Naively, I constructed this basic testmethod:

insert new Account(name = '00test'); // create test data here
Test.startTest();
  Test.setMock(HttpCalloutMock.class, myMultiMockObject); // setup of multiMockObject omitted for clarity
  MyBatchableClass bClass = new MyBatchableClass();
  Database.executeBatch(bClass,2);  // scope is 2
Test.stopTest();  // SFDC executes the async task here
System.assert(...) //verify results

When you execute this test, you get System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

I then tried to setup the test data in a different context like this:

System.runAs(someUserOtherThanRunningUser) {
   insert new Account(name = '00test'); // create test data here
}
Test.startTest();
  Test.setMock(HttpCalloutMock.class, myMultiMockObject); // setup of multiMockObject omitted for clarity
  MyBatchableClass bClass = new MyBatchableClass();
  Database.executeBatch(bClass,2);  // scope is 2
Test.stopTest();  // SFDC executes the async task here
System.assert(...) //verify results

When you execute this test, you get System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

Aargh. SFDC doc as of V30 wasn’t much help here. Much Googling ensued. Thanks to tomlogic for the solution essence.

Here is what you have to do –avoid the use of Database.executeBatch() in the testmethod if one of the batchable class’s start(), execute() or finish() methods does a callout. Thus, you have to invoke the start(), execute(), finish() methods explicitly to verify your batchable class logic and get code coverage.

insert new Account(name = '00test'); // create test data here
Test.startTest();
  Test.setMock(HttpCalloutMock.class, myMultiMockObject); // setup of multiMockObject omitted for clarity
  Database.BatchableContext 	bc;
  MyBatchableClass bClass = new MyBatchableClass();
  
  // we execute the start() and prepare results for execute()
  // in my use case, start() does the callout; 
  // thus the testmethod mocks the results of the callout (assumed here to be accounts)
  // setup of custom Iterable and Iterator not shown
  MyIterable  itrbl = (MyIterable)bclass.start();  //start() returns an iterable, in my case, a custom iterable. Note the casting
  MyIterator  itrator = (MyIterator) itrbl.iterator();  // continue simulation of start() by constructing the iterator
  List<Account> aScopeList = new List<Account> ();      // create scope for execute() by iterating against the result of the mocked callout
  while (itrator.hasNext()) {
     aScopeList.add(itrator.next());

  // Now invoke execute() w/ Account list built via mocked callout invoked by start()
  bClass.execute(bc,aScopeList);

  // Finally, invoke finish()
  bClass.finish(bc);
Test.stopTest();  // SFDC executes the async task here
System.assert(...) //verify results

To recap, this approach accomplishes the following:

  1. It will test code coverage as start(), execute(), and finish() are all invoked as if SFDC were invoking them by Database.executeBatch().
  2. It avoids the System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out
  3. It allows you to test against the returned data from the mocked callout(s) thus isolating your testmethod to predictable responses.
  4. It tests your custom iterator, if you are using one
  5. It only requires a bit more setup to individually invoke start(), execute(), and finish() plus manual construction of what execute() would get if database.executeBatch() were called