[SalesForce] Error using Database.Savepoint in Apex Test class

I have a test class with the following function in it:

@isTest
private static void testDequeue() {
    ... // a LOT of setup code here

    Test.startTest();
    Savepoint sp = Database.setSavepoint();
    String query = ...; // some query

    // CODE PART A
    JobA job = new JobA(query, false);
    Database.executeBatch(job, 10);
    List<ObjectA> records = [ ... ];
    System.assert(records.size() > 0);
    ... // a LOT more assertions
    Database.rollback(sp);

    // CODE PART B
    JobB job = new JobB(query);
    Database.executeBatch(job, 10);
    List<ObjectA> records = [ ... ];
    System.assert(records.size() > 0);
    ... // the same assertions as before
    Test.stopTest();
}

I'm testing two Jobs, JobA and JobB which both do the same thing. Both require the same setup code, which is why it is easier for me to test them in the same function. So, I'm using savepoints to make JobA do something, do some tests, then undo what JobA did, make JobB do the same thing, and then run the same tests.

The issue is, this code throws an AssertionError for the first assert statement:

System.AssertException: Assertion Failed

I've confirmed the Savepoint code is the reason, because when I remove it (as well as the PART B code), the error goes.

What's even more annoying is that, I've a lot of scenarios like this, and this code works for some, and fails for others. I'm not able to figure out why that happens. Is it a bug? Or is there something about savepoints I should know?

Best Answer

This wont work for a few reasons

  1. Without putting your batch invocations in separate startTest/stopTest blocks, you have no guarantee they will finish one after the other (they are two independent asynchronous transactions)
  2. Savepoint works per transaction and you can't rollback changes across transaction. In other words, each batch execute invocation can only role its own transaction back. There is an excellent post on this here: Rollback whole Batch

You should just make an @setup method and break your 2 tests into 2 methods with 2 startTest/stopTest blocks.

Related Topic