Saturday 5 November 2022

System.Assert Class in Winter 23

System.Assert

The Winter 23 release of Salesforce introduces a new Apex class in the System namespace - the Assert class. This contains methods to assert (or check) that the results of the code under test are as expected. 

We already have a collection of assert methods in the System class itself, so why do we need more? The surprising answer is that we don't! The existing assert methods can be purposed to confirm any condition you care to think of:

  • assert - confirm that a parameter evaluates to true
  • assertEquals - confirm two parameters evaluate to the same value
  • assertNotEquals - confirm that two parameters do not evaluate to the same value
In fact you could argue that we don't need all the methods that we have - the assert method alone with an appropriately constructed expression can confirm any behaviour.

The reason we have the new System.Assert class is the same as the reason that we were originally given the assertEquals and assertNotEquals - to provide clarity around the intent of our code. If we are interested in testing the equality of two variables then it's much easier to understand the intent using:
   System.assertEquals(firstValue, secondValue);
than
   System.assert(firstValue==secondValue);
The System.Assert class provides a number of new methods that allow you to write clearer unit tests, helping those that come after you get to grips with your code quicker

areEqual, areNotEqual 


These mirror the functionality of the existing System.assertEquals/assertNotEquals, but with more clarity - your code is verifying that the two parameters are equal or are not equal to each other.

isTrue/isFalse


Verify that the parameter evaluates to false or true. You could achieve the same thing using the existing methods, for example to check that the found variable is false:
          System.assert(!found);
          System.assertEquals(found, false);
          System.assertNotEquals(found, true);
      
but in each of these you hafe to look at the expressions that are used to generate the parameters, whereas with:
          System.Assert.isFalse(!found);
it's obvious what I'm trying to do

isNull/isNotNull


Verify the parameter passed is null or isn't null - this is more powerful than it might appear at first glance. Consider the following assertion:
          System.assertEquals(searchResults, 'null');
This looks reasonable, but only useful if you want to confirm that searchResults is a String with the 
contents 'null', rather than a null value. Sadly there's no way for me to determine which one the original author intended - instead I have to examine the code under test and figure out what the result should be Compare (see what I did there!) this with:
          System.Assert.isNull(searchResults);
and there's no room for doubt.

isInstanceOfType/isNotInstanceOfType


A slightly less obvious method, but one that you'll find very useful if you regularly find your unit tests catching exceptions and checking the correct one was thrown, handling collections of generic sObjects, or, like me, you've written a few classes that parse field history tracking tables and turn the old/new values back into their original data types.

Using the exception as an example, there's a few ways you can verify this with the old methods:
  • Only catch the specific type of exception you are expecting and let anything else cause the test to fail - not the greatest experience.
  • Catch the Exception superclass and use the instanceof operator to determine the actual type:
         System.assert(caughtException instanceof DMLException);
                
  • Catch the Exception superclass and use the getTypeName method to determine the actual type
         System.assertEquals(caughtException.getTypeName(), 'System.NullPointerException');
or use the new Assert class and make it very clear you are interested in the type of the parameter using:
          System.Assert.isInstanceOfType(caughtException, DMLException.class);

fail


Another method I'm particularly pleased to see. Often I'll be testing some code that should throw an exception, but I need a way to mark the test as a failure if it doesn't:

       try {
              // execute method
              System.assert(false, 'Should have thrown exception');
       }
       catch (Exception exc) {
           // expected behaviour - nothing to do
       }
    
To the casual browser, this looks like I'm verifying some behaviour after the method executes, and swallowing any exceptions that might be thrown - not a great test at all. The fail method gives me a mechanism to clearly indicate that if the code doesn't throw an exception then something is awry:
       try {
              // execute method
              System.Assert.fail('Should have thrown exception');
       }
       catch (Exception exc) {
           // expected behaviour - nothing to do
       }
    

Always Use Assert Messages


I'm guessing that we might have a few readers who are relatively new to Apex testing - the best piece of advice I can give you is to always use the variant of an assert method that takes a message parameter, and make that message useful. 

<comic-aside>
There's an old joke about a pilot flying a passenger in a small plane around a Seattle who experiences a navigation and comms outage . The pilot heads for a tall building with lit up offices, while the passenger writes "Where am I?" on a piece of paper and holds it up for the occupants to see. One grabs a piece of paper, writes on it and holds up the message "You are in a plane". The pilot immediately sets a course and lands safely a couple of minutes later. The passenger asks how the message made a difference, and the pilot replies "The information was 100% accurate and no help at all, so I knew that was the Microsoft support building".

</comic-aside> 


Consider the following test :
      Integer pos=2;
      System.Assert.areEqual(3, pos);
Upon running this, you'll get the following output:

System.AssertException: Assertion Failed: Expected: 3, Actual: 2

Much like 'You are in a plane', this is 100% accurate and no help at all to someone who isn't intimately familiar with the codebase. The message parameter gives you an opportunity to provide some accurate and helpful information, for example:
    Integer pos=2;
       System.Assert.areEqual(3, pos, 
                        'The matching record should be found at position 3 of the list');
  
Which gives the output:

System.AssertException: Assertion Failed: The matching record should be found at position 3 of the list: Expected: 3, Actual: 2
One final word of advice - always remember the message is describing the error, not the successful outcome. You'd be surprised how many times I've seen something like the following:
System.AssertException: Assertion Failed: The matching record is at position 3 of the list: Expected: 3, Actual: 2
when it really isn't, that would be the case if the test passed!

Related Information




3 comments:

  1. Bob,

    Why prefix the Assert with system when it is not needed? Is it habit after years of the old way or a convention?

    Thanks for all the great content!

    ReplyDelete
    Replies
    1. In this case it was for the sake of clarity again, as the title of the post was the System.Assert class.

      Delete
  2. Great post!
    But my opinion on the new Assert class is that it will bring more confusion than benefits, see
    my comment on the topic here: https://beyondthecloud.dev/blog/system-assert-class-vs-system-assert-equals-methods

    ReplyDelete