3

On our community website we have some logic which throws exception when some set of conditions is invalid. This exception is not handled by any part of our code. As result system rollback happens automatically (that's exactly what we want), plus our custom exception page is displayed to user.

Everything works fine.

But now I'm trying to write tests for this business process. And that's where the fun begins. Bellow is the skeleton of my test method:

// assert data: state_1

try {

    Test.startTest();

        MyController.doActionWhichCausesUnhandledException();

        System.assert(false);

    Test.stopTest();

} catch (MyBusinessException exp) {

    // assert exception message
    ...

    // assert data: still should be in state_1
    // but it fails here
    // cause all stuff created in the controller haven't been rollbacked

}

Based on my expectation if business exception crosses this test start/stop block, it should causes rollback of everything inside that block. But that's not the case, nothing is rollbacked and data assertions failed due to partially updated/created data inside controller action.

The only idea I've come up so far is to use savepoint and explicit rollback right inside test method, which doesn't make a lot of sense from testing perspective, but it actually rollbacks data)

The question here: is it possible somehow to test this rollback behaviour in a better way? Any help would be appreciated. And thanks in advance for your help.

5

One could argue that you don't need to test explicitly for uncaught system exceptions and subsequent system rollback because that is SFDC's job to verify this works, not yours. A rough analogy to this is you don't need to verify SFDC can send the email as long as you don't get an error/exception using Apex outbound email sendEmail(). Or, you don't need to verify that if you redirect to a page, that SFDC actually displays that page.

Your testmethod, because it is catching the exception that your production code is not, won't cause a rollback.

An alternative argument is whether your Production Code should try and catch every possible exception - for VF this is hard to do as you need elaborate coding in each getter/setter as well as within action methods (where it is easier)

4
  • 2
    This is exactly correct. It's not our job to unit test standard, built-in functionality. We must simply assert that our code is indeed throwing the exception we expect. – sfdcfox Aug 1 '16 at 17:10
  • In a way I could agree with you, that we don't need to test for this. Basically, I've removed my data assertion and only testing now for exception to happen. But if we think about this in a broader way: my business requirement is "not to create/modify any date in some particular case", the implementation could be different (e.g. system rollback, custom rollback etc.) and it could change with time. So I'd rather test against business requirement (data is still the same), not against specific implementation (exception is thrown). But I got it, there is no way, at least not in SF world. – wesaw Aug 1 '16 at 17:25
  • @wesaw You could use Selenium to verify the exception page appears – cropredy Aug 1 '16 at 17:43
  • What about testing that the Database.rollback was executed in the catch? – Scott Pelak Oct 7 '19 at 18:50
0

Did you figure out that Test.stopTest(); is not reached if an exception occurs?

Try adding an other Test.stopTest(); after catch.

// assert data: state_1

try {

    Test.startTest();

        MyController.doActionWhichCausesUnhandledException();

    Test.stopTest();

    System.assert(false);

} catch (MyBusinessException exp) {
    Test.stopTest();

    // assert exception message
    ...

    // assert data: still should be in state_1
    // but it fails here
    // cause all stuff created in the controller haven't been rollbacked

}
5
  • Still no rollback. If you think about this, now exception is handled inside test block, like in business logic, and it doesn't go outside) – wesaw Aug 1 '16 at 13:27
  • Let me understand, inside doActionWhichCausesUnhandledException are you doing rollback with Database.rollback(sp);? or simply data is rollbacked due to exception? If you have multiple insertions or updates, only the data of the insertion failed is rollbacked, all other previous insertions / updates are not rollbacked. – D. Cruz Aug 1 '16 at 13:47
  • @DCruz There is no Database.rollback(sp) inside controller actions. There is a bunch on insert and update statements and then throw new MyBusinessException(). When I tested this manually through our community website all stuff is rollbacked by Salesforce itself. I'm trying to write apex test for this to automate this checking. – wesaw Aug 1 '16 at 13:55
  • Theoretically when one insert/update fails the previous ones are not rollbacked and the subsequent insert/update are not executed. But if the first insert is the one that fails... maybe this is the reason why when you test manual it seems all data is rollbacked. – D. Cruz Aug 1 '16 at 14:25
  • @DCruz You are completely wrong. When Salesforce encounters with unhandled exception, the whole transaction is rollbacked. It doesn't matter how many records/objects have been already updated/created/deleted, and which exactly DML operation failed. It could be the 99th, still all 99 would be rollbacked. – wesaw Aug 1 '16 at 16:59
0

I much prefer a different approach. You should never use assert(false) in your production level tests in my opinion. Because in a broader sense, I believe your tests should not contain condition assertions at all. Every assertion in every test should run, and should always run the same way.

MyBusinessException expectedException;
Test.startTest();
    try
    {
        MyController.doActionWhichCausesUnhandledException();
    }
    catch (MyBusinessException e)
    {
        expectedException = e;
    }
Test.stopTest();

system.assertNotEquals(null, expectedException, 'message');

This way, you always assert the same thing. I feel it's much cleaner.

4
  • That's arguable, but anyway it doesn't help solving rollback problem at all) – wesaw Aug 1 '16 at 13:59
  • @wesaw If your expectedException is null, it's a data setup problem. – Adrian Larson Aug 1 '16 at 14:01
  • My expectedException is not null. The problem is that by throwing this exception to the Salesforce itself (i.e. without handling in our custom code), the data rollback should happen. It happens when we test manually from UI. I'm trying to automate this test in the form of unit tests. Your suggestion is more like preference of coding and has nothing to do with rolling back and posted question, sorry( – wesaw Aug 1 '16 at 17:06
  • As mentioned by @cropredy, catching the exception still avoids the rollback. And you're testing system functionality. Don't. – Adrian Larson Aug 1 '16 at 17:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.