Why use Exception handling?

Many times, custom classes will have expected behavior and expected errors for improper usage of code. This means there is rigidly defined expectations for failures, and not just for when using the code "properly." In order to thoroughly test these classes, then, you must write tests against the error cases '''as well as''' the proper use cases, ensuring that both successful and unsuccessful use cases behave as expected.

Exception Handling Defined

Exception handling in tests allows you to test against known errors by using the expects metadata attribute, thus turning what would normally be considered a failure in FlexUnit 4.x into a success-criteria.

Working through an example

In most cases, tests are verifying that appropriate use cases of a bit of code do what they're supposed to do. For example, let's say you have a class with the following method:

public class ObjectCreationManager {

public static function createObjectByType( type : String ) : Object
{
     if ( !type )
          throw new CustomErrorType( "Type not specified!" );

     switch ( type )
     {
          case ( "Type1" ) :
          {
               return new Type1Object();
               break;
          }
          case ( "Type2" ) :
          {
               return new Type2Object();
               break;
          }
          case ( "Type3" ) :
          {
               return new Type3Object();
               break;
          }
          default :
          {
               throw new CustomErrorType( "Invalid type specified!" );
               break;
          }
     }
     
     return null;
}

}

Obvious candidates for [TestMethod] ("unit tests") would include methods for each valid string type (ie: "Type1", "Type2", or "Type3"), verifying that passing each in would return an instance of the expected object class (ie: Type1Object(),
Type2Object(), or Type3Object() ).

An example of these tests would be:

[Test(description="This test verifies that ObjectCreationManager.createObjectByType() will return an object of type 'Type1Object' if passed a string with the value 'Type1'")]
public function createObjectByType_Type1() : void
{
     var typeStr : String = "Type1";
     var typeObj : Object = ObjectCreationManager.createObjectByType( typeStr );
     Assert.assertTrue( typeObj is Type1Object );
}

However, looking at the createObjectByType() method, there are two other expected behaviors, namely: expected failures with correlative expected errors. To get full code coverage in your tests, you should verify that expected errors are thrown under the circumstances in which they should be thrown.

To test this, you can take advantage of FlexUnit 4.x's expects metadata attribute. Adding this metadata attribute with the expected error type informs FlexUnit that an error of said type is expected to be thrown during the execution of the test method.

An example of this would be:

[Test(description="Verifies that ObjectCreationManager.createObjectByType() will throw an error if passed an invalid type string", expects="somePackage.CustomErrorType")]
public function createObjectByType_Type1() : void
{
     var typeStr : String = "TypeFAKE";
     var typeObj : Object = ObjectCreationManager.createObjectByType( typeStr );
}

By defining an expected error, this changes the criteria for success and failure of the test. Now this test will pass '''only if''' the error in the expects attribute, defined by the '''fully-qualified class name''' is thrown during the execution of the test method. If this specific type of error is not thrown, the test fails.

It is worth reiterating that the expects metadata attribute is defined by a fully-qualified class, and '''not''' by error message. Yet another reason why custom error classes are important to use, rather than a standard error with a custom message.

  • No labels