3

I am looking at testing the following exception message.

public static void addError(String msg){
    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, msg));
 }  

public PageReference save(){
try{...}
catch(Exception e){
      addError(e.getMessage());
}

I have already tested the addError method but want to know how I can cover: addError(e.getMessage). How can I make the exception occur in a test class and assert the message that is displayed?

1

You need to find some way to provide the content of the try block with bad input so that it'll generate an exception.

For example, if it's saving a public SObject, try and set that object to null before calling the save method.

You can use Test.IsRunningTest() to determine if the code is being run as a test and manually throw an exception, but I prefer not to modify my code behaviour expressly for the purposes of test coverage, I choose to take the slightly lower coverage instead.

Often catch blocks are a one liner like this, and because of the way the code coverage processor works (it counts the braces as a line) you can improve your coverage count by just changing the formatting of the block from:

catch(Exception e){
      addError(e.getMessage());
}

to:

catch(Exception e) {addError(e.getMessage());}

Yes it's hacky, far from perfect and kind of defeats the point of test coverage, but then again I'm more than happy to trust that line of code to work when it needs to accept that I'm dealing with a less than perfect testing tool.

I recently wrote a blog post regarding my philosophy for Salesforce test methods.

  • I did make Case newCase=null but then I get: System.NullPointerException: Attempt to de-reference a null object. I also extending the Exception class and threw an error manually. But it shows that the error was not thrown inside the method save() so that line is still not covered. I implemented your solution and increased my coverage with 2% whoohooo!Thanks. – Thys Michels May 21 '13 at 4:49
2

Use the ApexPages.GetMessages() method. So your test method should look something like this:

public static testMethod void testMyController() {
    PageReference pageRef = Page.MyPage;
    Test.setCurrentPage(pageRef);

    thecontroller controller = new thecontroller();
    controller.save();

    System.assertequals('my error message',ApexPages.GetMessages().get(0).getSummary());
}
  • The exception is not thrown and get: System.ListException: List index out of bounds: 0 – Thys Michels May 21 '13 at 3:33
  • @ThysAndriesMichels that was just a sample. Like LaceySnr said you'll need to set up your controller so that the try block in the save method throws an exception. – Greg Grinberg May 21 '13 at 14:32
  • Thanks Greg, I can't find a easy way to setup my controller without apex throwing an null pointer exception and does not get caught. Do you have an easy way I can make my controller throw an error that can be caught? – Thys Michels May 21 '13 at 17:39
  • @ThysAndriesMichels It would really depend on the details of your controller and the save method. Can you get the page to throw an exception on save through normal usage? Maybe by changing the record you are saving so that it fails a validation rule or something like that. – Greg Grinberg May 21 '13 at 18:20
1

My solution to this, especially for situations where exception catching does rollbacks that I want to verify in my test coverage is as follows:

First, a class FlowControl with static variables:

public without sharing class FlowControl {
  public enum   ForceFailure {FOO_FAIL,BAR_FAIL}; // add enums as needed
  private static Map<ForceFailure,Boolean>  forceFailureToEnabledMap = new Map<ForceFailure,Boolean>();
  static {resetAll();}
  public static boolean isForceFail(ForceFailure executionUnit) {return forceFailureToEnabledMap.get(executionUnit);}
  public static void setForceFail(ForceFailure executionUnit)   {forceFailureToEnabledMap.put(executionUnit,true);}
  public static void resetAll() {
    for (ForceFailure ff : ForceFailure.values())
        forceFailureToEnabledMap.put(ff,false);
    }
}

Then, in my code myFooMethod() where I care about getting test coverage on the catch block:

... code within myFooMethod()
try {
  // some code
  // just before the DML statement
  if (FlowControl.isForceFail(FlowControl.ForceFailure.FOO_FAIL)) {Integer i = 10 / 0;}  // divide by zero
  update someObj;
  }
catch (Exception e) {/* my exception code ... */}

And finally, in the testmethod that tests error handling:

...
FlowControl.setForceFail(FlowControl.ForceFailure.FOO_FAIL);
myFooMethod();
System.assert(/* my assertions to verify catch block did the right thing */ );

The advantages to this are:

(1) I can customize the exception thrown per use case. The example above throws a divide by zero exception because the catch block doesn't care what exception occurs in its catch logic. Your could create bogus ID fields, modify where clauses or force a known SObject validation rule to fail.

(2) I can set up multiple exception 'test triggers' that don't step on each other during a testmethod execution that spans multiple try-catch blocks scattered throughout your code

(3) A reset method to use when simulating within a single testmethod multiple end user interactions separated by page redirects or API events.

(4) You can get to the magic and satisfying 100% code coverage stats.

The disadvantage, of course, was already noted - your PROD codebase has code inserted to assist testcoverage - simple code and benign, but still unnecessary for PROD operations.

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.