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!
Bob,
ReplyDeleteWhy 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!
In this case it was for the sake of clarity again, as the title of the post was the System.Assert class.
DeleteGreat post!
ReplyDeleteBut 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