Use of ASSERT
I agree with the principle of Dump for totally unrecoverable situations
However, I much prefer using ASSERTs. Yet they are not mentioned anywhere in the guide?
IF foo IS INITIAL.
MESSAGE x666(general). "Should never happen!
ENDIF.
vs.
ASSERT foo IS NOT INITIAL.
I must confess I also (mis?)use ASSERT 1 = 2. to force a dump. I don't use MESSAGE because it belongs to the classic dynpro world and the doco is pretty clear about not using it, and even explicitly advises against the above example:
https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-US/index.htm?file=abenmessages_guidl.htm
Sometimes I'll use ASSERT only to avoid the compiler complaining about an uncaught exception. There are occasions I want an exception to dump, but to keep the syntax check happy I have to do a clumsy catch-and-dump. A typical use case is:
TRY.
data(guid) = cl_system_guid=>create_uuid_32_static( ).
CATCH cx_uuid_error.
"We have bigger problems if this failed
ASSERT 1 = 2.
ENDTRY.
Thoughts? Should some recommendations on ASSERT be included in the guide?
Agreed, personally, every time I would comment "should never happen", it becomes an ASSERT.
Using the ASSERT ID .... CONDITION ... variant might be better though as that enables you turn assertions on/off in different environments.
If you want to write fault tolerant software where the user might not have access to the backend, then you want to transport an error message to the frontend. Also, you can not test ASSERT statement with unit tests.
@thomham by that logic you are equally suggesting that message type X should also not be used and the whole guideline should be removed. However I think it makes sense, and the author placed good emphasis on using it with great care.
We should raise exceptions for everything we can realistically expect to go wrong, but it's unrealistic to catch every possible thing that can go wrong. Dumps/asserts are for that grey area in between: highly unlikely situations that don't warrant explicit handling but where it would be dangerous to continue. I also use them for programming errors - e.g. to stop someone from calling a method in an incorrect way; the type of situations that should be caught by the most basic testing.
Regarding ASSERT and unit test, I've grappled with this before, see Is it possible to test ASSERT in ABAP Unit?
Anyway, the point of this issue was to suggest that an assertion is better than message type X receommended by the guideline. And since you raise the point, here's what the responses look like in a web environment:
500 SAP Internal Server Error ERROR: The current application has triggered a termination with a short dump. (termination: RABAX_STATE)
vs.
500 SAP Internal Server Error ERROR: The ASSERT condition was violated. (termination: RABAX_STATE)
From a user perspective, I would think that the ASSERT variant is marginally less frustrating as it provides that tiny bit more explanation that something went wrong.
Here some more arguments which came out of another discussion about ASSERT and how and if to include it into the clean code document:
-
The two
ASSERTvariants - with and withoutID- behave completely different and should therefore be looked at differently. While the plainASSERTtriggers a runtime exception which can not be handled, theASSERT IDvariant can be switched off via configuration. The latter feels more like "cranking up the log level to debug". -
It's hard to unit-test an
ASSERT. If the condition is okay, the assertion goes unnoticed. If it fails, it results in a runtime error that cannot be managed by the unit test framework, such that it is not possible to write a unit test that explicitly provokes this failure. You can create a test for the private method called by theASSERTto make sure the detection works, to avoid false positives or false negatives. For a simpleASSERT sy-subrc = 0. I think we can spare automatic tests. If it should have been a<>, you will find out immediately anyway. -
From a readability perspective,
ASSERTcan serve as "executable comments" that tell the reader a little more about the program's current expected state. -
ASSERTseem to fit better to places where my code calls something else, to validate that what happened in those calls matches my expectations, and if not, to uncover programming errors on my side. On the other side, any behavioural change in the called functionality might require de-central rework in all usages. I would prefer having exactly these checks as unit tests on the called method. -
They seem less fit to validate a method's input at the method's start, as the code is likely unable or unwilling to recognise whether the input is internal or external or comes from a program or human user. For these pieces, exceptions seem to be the better choice, for example because they allow the caller to catch the exception, adjust the input, and repeat the call.
For the use of assertions the guidance given by Code Complete (chapter 8.2 in the second edition) is the one I like best:
Use error-handling code for conditions you expect to occur; use assertions for conditions that should never occur Use assertions to document and verify preconditions and postconditions
Like the executable comments. Your reader (vacation substitute, development support) can identify that you considered the situation and their next job is to find out whether your assumption was faulty or code running before yours is.
For highly robust code, assert and then handle the error anyway
I see a difference between assertions and exceptions, for example, when it comes to preconditions of a method. I stick to this specific topic. With an assertion I describe a contract any caller needs to adhere to. Violation of the contract is similar to a programming error. (The 'inconsistency' some colleagues mentioned above.) If the signature of a method is clear enough and unambiguous then there is no need for additional assertions. Exceptions are the result of input validations (the value is provided by a user or another system or derived from those input) when the caller needs to be informed about invalid input in that specific case. Not sure if the difference becomes clear: one identifies wrong 'calls' of a method in the sense of wrong programming, the other wrong input. The problem often is that the provider of a method cannot really distinguish between the two. Therefore, exceptions are used in most cases.