6

I have a class that includes the following:

    try {
        stuff....
    }catch(Exception e){
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, e.getMessage()+'ERROR'));
    }
}

I'm trying to catch the error message in a test class to bring the code coverage to 75%.

        try {

    STUFF to PURPOSEFULLY BREAK...... 
    System.assert(false, 'Exception expected');
    } catch (DmlException e) {
        String message = e.getMessage();
        System.assert(e.getMessage().contains('ERROR'), 'message=' + e.getMessage());
    }

Yet nothing happens.

2
  • 1
    The answers below are great. But may I just point out that your code is not throwing any exception that your test code will catch? Instead of a try-catch, your code needs to test for the presence of messages in the ApexPages.Messages collection – Sebastian Kessel May 20 '16 at 15:23
  • You can't catch assertions – Scott Pelak Apr 14 '18 at 16:01
10

You should really know a more specific type of Exception. If you are working out how to get the try block to throw an exception at all, see the bottom section.

If you know calling a method will cause an Exception and want to make sure it is handled by that method, your assert(false) should be in the catch block, not try. A common pattern is:

try
{
    callMethodWithHandledException();
}
catch (DmlException unexpectedException)
{
    system.assert(false, 'Exceptions should be handled by the method');
}

I really dislike this pattern, though, and instead tend to prefer:

DmlException unexpectedException;

try
{
    callMethodWithHandledException();
}
catch (DmlException dmx)
{
    unexpectedException = dmx;
}

system.assertEquals(null, dmx, 'Exceptions should be handled by the method');

Using this pattern, you always hit the desired assertion, which I see as a notable advantage.


Original

You should never break your production code (even conditionally) just for coverage. If you are trying to actually hit the catch block, do so by mocking erroneous data in your test. The canonical example would be a DmlException.

public with sharing class MyExtension
{
    public MyObject__c record { get; private set; }
    public MyExtension(ApexPages.StandardController controller)
    {
        this.record = controller.getRecord();
    }
    public PageReference myInsert()
    {
        try
        {
            insert record;
        }
        catch (DmlException dmx)
        {
            ApexPages.addMessages(dmx);
            return null;
        }
        return new ApexPages.StandardController(record).view();
    }
    public PageReference myUpdate()
    {
        try
        {
            update record;
        }
        catch (DmlException dmx)
        {
            ApexPages.addMessages(dmx);
            return null;
        }
        return new ApexPages.StandardController(record).view();
    }
}

The way to hit the catch block is not to monkey around with your try block. Instead, you can pass a record you know will fail to update. For instance, one which has no Id. The inverse works, too; if testing insert, specify an Id.

static testmethod void testMyInsert_Error()
{
    MyObject__c record = new MyObject__c();
    insert record; // cannot insert it again
    ApexPages.StandardController controller = new ApexPages.StandardController(record);
    MyExtension extension = new MyExtension(controller);

    Test.startTest();
        PageReference redirect = extension.myInsert();
    Test.stopTest();

    system.assert(ApexPages.hasMessages(), 'There should be an error');
    system.assertEquals(null, redirect, 'Redirect should be cancelled');
}
static testmethod void testMyUpdate_Error()
{
    MyObject__c record = new MyObject__c(); // cannot update this record
    ApexPages.StandardController controller = new ApexPages.StandardController(record);
    MyExtension extension = new MyExtension(controller);

    Test.startTest();
        PageReference redirect = extension.myUpdate();
    Test.stopTest();

    system.assert(ApexPages.hasMessages(), 'There should be an error');
    system.assertEquals(null, redirect, 'Redirect should be cancelled');
}
0
2

With the little code that you've provided, it's impossible to determine exactly what you need to be doing to gain coverage for your SUT's (system under test) catch block.

The general answer is that your provided code is causing an exception to be thrown from your test class rather than from inside the code that you're testing. You need to present your SUT with an environment where it is guaranteed to throw your exception.

You're also handling the exception in your SUT, so your test class wouldn't be looking to catch an exception, but rather detect the message being added to ApexPage using ApexPages.getMessages() (not sure if Test.setCurrentPage() is also required in this case).

1

You are trying to catch a DmlException. Is the "STUFF TO PURPOSEFULLY BREAK" throwing a DmlException specifically? If not, then it will never enter the catch block.

To create a DmlException, you can always just try to do an insert on an account without the name field: insert new Account();

That should fail with a DmlException.

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.