1

I have some apex code, where I try to upsert a list of records:

try {
       upsert recordList Accounts;
} catch (DmlException e) {
            //do a loooot of stuff in order to provide a proper error message
            //which will be returned to the component
  }

Now I built two test scenarios in its respective test class.

The one will be able to upsert the list. Works fine of course.

The other one (which is supposed to cover the catch block) won't because I porpously try to add duplicate values for a unique field. But instead of covering the catch block, the test class run will result into a failed one, becuase it will run into a System.AuraHandledException: Script-thrown exception and return the catch blocks error message in the dev console log, instead of happily cover the catch block.

Is there a way to do it in a cleaner way?

  • 1
    It's a bit hard to tell what you are actually asking here. Is your primary complication just that your catch block is designed to bubble up a different exception type? – Adrian Larson Oct 16 at 14:37
  • The primary issue is that the catch part is not covered by the test, but instead my test run results into a failed test run. – Cmd_Keen Oct 17 at 8:04
2

If you are trying to verify that your exception bubbles up as AuraHandledException, I typically prefer a pattern as follows:

// set up data

AuraHandledException expectedException;
Test.startTest();
    try
    {
        // do stuff
    }
    catch (AuraHandledException e)
    {
        expectedException = e;
    }
Test.stopTest();

system.assertNotEquals(null, expectedException, 'DMLException should bubble up to the Aura framework');
  • yep, that worked, now try and catch is successfully covered via test run. thx – Cmd_Keen Oct 17 at 8:39
1

Personally I would put all the code in the catch block into a separate method that accepts the exception and any other state that is needed as its parameters (it can also return a value if required). Then I would write a unit test just for that method and not worry about the single uncovered line (the call to this method) from the catch block in the first method.

try {
    upsert recordList Accounts;
} catch (DmlException e) {
    handleException(e);
}

And:

@TestVisible
private void handleException(DmlException e) {
    //do a loooot of stuff in order to provide a proper error message
    //which will be returned to the component
}

Then write a unit test for the first method containing the upsert and a separate one for "handleException".

There are other ways to deal with this, including use of a Test.isTestRunning block in a trigger for the objects being inserted that uses some static state to determine if it should throw an exception, for example, but I don't really see the need since all that does it give you coverage on 1 extra line.

  • good idea, unfortunately I cannot utilize e.getDmlIndex(0) within this extra method for some reason. – Cmd_Keen Oct 16 at 14:50
  • exactly, the Test.isTestRunning doesn't do the trick for me in this case. – Cmd_Keen Oct 16 at 14:52
  • 2
    If your problem is you are trying to access methods on DmlException, then simply change your helper method parameter to DmlException instead of the generic Exception. – Adrian Larson Oct 16 at 14:54
  • yep, of course I needed to change it to DmlException. Thx However, if I want to test this class now, I have to prepare an exception which I can pass to the test method. Is something like DmlException testException = new DmlException(); and then in test method priceLimitsController.handleExceptionSep(testException); correct? This testclass leds now into a internal salesforce error. – Cmd_Keen Oct 17 at 7:30
  • Yes, you are right. You would need to "mock" the DmlException. Normally this would be done using something like JSON.deserialize with appropriately crafted JSON (where you can't construct the object with details directly). However, it seems the DmlException is written to disallow JSON serialize/deserialize and does not allow construction of the exception with meaningful details. That means this approach cannot be applied directly in this case. Note that this approach is, in general, good for testability of code, just not in this case. See @AdrianLarson's answer instead. – Phil W Oct 17 at 8:02
1

Further to @AdrianLarson's suggestion, the variant on this pattern that I use is:

// Set up the test data
...

try {
    // Do the stuff you want
    ...

    // Directly after where we expect an exception, add:
    System.assert(false, 'Expected an exception to be thrown');
} catch (TheExceptionTypeExpected e) {
    // Inspect the exception's details and apply any required asserts against them
    ...
} catch (Exception e) {
    System.assert(false, 'Wrong type of exception thrown: ' + e);
}

You can include the start/stop test around this as needed. What I like about this slightly more verbose structure is the ability to provide more detail and different assert messages.

  • 1
    @AdrianLarson's one did the trick, but this is a good enhancement. thx – Cmd_Keen Oct 17 at 8:40
  • 1
    I would still separate the exception handling code out into a separate method - it's good practice to keep individual methods short and does promote testability in general. – Phil W Oct 17 at 8:49
  • It's not an enhancement, it is a different pattern. Some might argue it is the opposite (of an enhancement), as my firm axiom for assertions is you should only write them if they will be made every single test run. IMO it makes for cleaner, stronger tests. – Adrian Larson Oct 17 at 13:43

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.