4

Let's say we have some class MyClass:

public class MyClass {
    public void doSomething {
        // do some stuff

        try {
             OtherClass.staticMethod();
        }
        catch (OtherClass.BadException e) {
            // do something with the exception
        }

        // do some other stuff
    }
}

I know that OtherClass.staticMethod can throw an exception, but:

  • the circumstances in which it might do so are not within the purview of this class or its associated unit tests
  • OtherClass has its own unit tests that are (presumably) already testing its functionality and exception handing; MyClass shouldn't have to duplicate those tests
  • OtherClass might be a black box, into which I have no insight; finding conditions that would trigger an exception would be a matter of guessing and testing

The purist in me wants to keep the unit tests implementation-agnostic, which in this case means that they shouldn't care why OtherClass.staticMethod would fail (or even that MyClass references OtherClass at all) - I just want to catch an exception.

Is there a way to generically handle this case? The closest I have seen is something like this:

public class MyClass {
    public boolean throwUnitTestExceptions = false;
    public void doSomething {
        // do some stuff

        try {
             if (Test.isRunningtest() && throwUnitTestExceptions) {
                 throw new OtherClass.BadException();
             }
             else {
                 OtherClass.staticMethod();
             }
        }
        catch (OtherClass.BadException e) {
            // do something with the exception
        }

        // do some other stuff
    }
}

I could then set the throwUnitTestExceptions flag when unit testing the exception handling.

This leaves a bad taste in my mouth, but it would work. Is there a better way that doesn't require knowing the details of OtherClass's implementation to establish conditions that would throw an exception?

2
  • IMO, I think that having black box static methods, that can throw exceptions, is bad idea. Because exceptions are not a way to transfer some data. Those mechanism is called returned value. No one should be afraid of exceptions that appears inside. Those static method should care about that itself. So, in your place, I would create class called OtherClassMethodResult, that would include information about exceptions and probably something else. And after that you can create method that perform check based on returned instance of this class, and generate as many test responses as you really need.
    – kurunve
    Jan 6 '16 at 22:22
  • The static method was just an example so I didn't have to write out code to instantiate the object. In my case, I don't actually have any black-box classes that are known to throw exceptions, but it is still a possibility.
    – asgallant
    Jan 6 '16 at 22:35
5

You should probably not try to test the exception branch, and just accept the fact that you'll have less than 100% code coverage. This is based on three concepts: (1) you shouldn't include test-only code in a production code branch, (2) exception testing is sometimes non-trivial or even impossible to perform, so you simply have to statically verify (by hand) that the exception should be handled in a graceful manner, and (3) code coverage of 100% is often elusive (because of the prior reason), so one should settle for some value that is well above 75% but less than 100% (my personal target is 90%). If you can cause "otherClass" to throw an exception, great. If not, you should probably just accept that sometimes you can't test something.

While I'm not a big fan of leaving code untested, I am a big fan of being able to ship my code in a reasonable amount of time, and have that code perform as efficiently as possible. Technically, you could just not include error handling at all; the platform will still bubble up the exception in a way that the user can say "something bad happened, here was the error." Of course, the user will probably lose whatever they were working on, so I'd rather leave the exception handling untested rather than not caught at all. Ideally, you can minimize the amount of untested code you have by having a utility class that can transform an exception into a meaningful error message. This means that your code will have a minimum amount of untested code while still offering exception handling.

try {
    otherClass.staticMethod();
} catch(Exception e) {
    Utils.handleException(e);
}

You can then independently test the Utils class to get 100% in that class, while still giving yourself the best possible coverage in the class you're trying to cover. Of course, you'll probably want various types of exception handling methods, such as ones that would report an error via ApexPages.addMessages, one that would use addError on an SObject, etc.

2
  • I'd love to just be able to leave it untested, but so much of the project I am working on falls under the "you just can't unit test that in salesforce" that I am constantly flirting with the 75% code coverage requirements.
    – asgallant
    Jan 6 '16 at 22:39
  • @asgallant I feel your pain. Usually, when I'm faced with continual 75% threats, I start refactoring code to introduce fewer exception paths. For example, changing if(blah) return; might become if(!blah) { dosomething(); }. I'm also now toying with the idea of releasing a utility method that can be used to ease exception handling in these cases. It'd make a great addition to the library I'm working on.
    – sfdcfox
    Jan 6 '16 at 22:43
3

Yes there is a way to test without worry about the OtherClass implementation. What you're looking for is ApexMocks. https://github.com/financialforcedev/fflib-apex-mocks

It's worth checking out all of the article links in the repo's README.

In short, this framework allows you to stub out methods in OtherClass to 1) return a specific value with a specific parameter and 2) throw a specific exception with a specific parameter.

0

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.