Java assertions underused

前端 未结 11 1941
独厮守ぢ
独厮守ぢ 2020-12-04 21:16

I\'m wondering why the assert keyword is so underused in Java? I\'ve almost never seen them used, but I think they\'re a great idea. I certainly much prefer the

相关标签:
11条回答
  • 2020-12-04 21:22

    assertions are, in theory, for testing invariants, assumptions that must be true in order for the code to complete properly.

    The example shown is testing for valid input, which isn't a typical usage for an assertion because it is, generally, user supplied.

    Assertions aren't generally used in production code because there is an overhead and it is assumed that situations where the invariants fail have been caught as coding errors during development and testing.

    Your point about them coming "late" to java is also a reason why they aren't more widely seen.

    Also, unit testing frameworks allow for some of the need for programmatic assertions to be external to the code being tested.

    0 讨论(0)
  • 2020-12-04 21:33

    As others have stated: assertions are not appropriate for validating user input.

    If you are concerned with verbosity, I recommend you check out a library I wrote: https://github.com/cowwoc/requirements.java/. It'll allow you to express these checks using very little code, and it'll even generate the error message on your behalf:

    requireThat("name", value).isNotNull();
    

    and if you insist on using assertions, you can do this too:

    assertThat("name", value).isNotNull();
    

    The output will look like this:

    java.lang.NullPointerException: name may not be null
    
    0 讨论(0)
  • 2020-12-04 21:34

    I'm not sure why you would bother to write asserts and then replace them with a standard if then condition statement, why not just write the conditions as ifs in the first place?

    Asserts are for testing only, and they have two side effects: Larger binaries and degraded performance when enabled (which is why you can turn them off!)

    Asserts shouldn't be used to validate conditions because that means the behaviour of your app is different at run time when asserts are enabled/disabled - which is a nightmare!

    0 讨论(0)
  • 2020-12-04 21:35

    In "Effective Java", Joshua Bloch suggested (in the "Check parameters for validity" topic) that (sort of like a simple rule to adopt), for public methods, we shall validate the arguments and throw a necessary exception if found invalid, and for non-public methods (which are not exposed and you as the user of them should ensure their validity), we can use assertions instead.

    0 讨论(0)
  • 2020-12-04 21:36

    tl;dr

    • Yes, use assertion-testing in production where it makes sense.
    • Use other libraries (JUnit, AssertJ, Hamcrest, etc.) rather than the built-in assert facility if you wish.

    Most of the other Answers on this page push the maxim "Assertions aren't generally used in production code”. While true in productivity apps such as a word-processor or spreadsheet, in custom business apps where Java is so commonly used, assertion-testing in production is extremely useful, and common.

    Like many maxims in the world of programming, what starts out true in one context is misconstrued and then misapplied in other contexts.

    Productivity Apps

    This maxim of "Assertions aren't generally used in production code”, though common, is incorrect.

    Formalized assertion-testing originated with apps such as a word-processor like Microsoft Word or a spreadsheet like Microsoft Excel. These apps might invoke an array of assertion tests assertions on every keystroke made by the user. Such extreme repetition impacted performance severely. So only the beta-versions of such products in limited distribution had assertions enabled. Thus the maxim.

    Business Apps

    In contrast, in business-oriented apps for data-entry, database, or other data-processing, the use of assertion-testing in production is enormously useful. The insignificant hit on performance makes it quite practical – and common.

    Test business rules

    Verifying your business rules at runtime in production is entirely reasonable, and should be encouraged. For example:

    • If an invoice must have one or more line items at all times, then write an assertion testing than the count of invoice line items is greater than zero.
    • If a product name must be at least 3 characters or more, write an assertion testing the length of the string.
    • When calculating the balance for a cash ledger, you know the result can never be negative, so run a check for a negative number signaling a flaw in the data or code.

    Such tests have no significant impact on performance in production.

    Runtime conditions

    If your app expects certain conditions to always be true when your app runs in production, write those expectations into your code as assertion tests.

    If you expect those conditions may reasonably on occasion fail, then do not write assertion tests. Perhaps throw certain exceptions. Then try to recover where possible.

    Sanity-checks

    Sanity checks at runtime in production is also entirely reasonable, and should be encouraged. Testing a few arbitrary conditions that one could not imagine being untrue has saved my bacon in countless situations when some bizarre happening occurred.

    For example, testing that rounding a nickel (0.05) to the penny resulted in a nickel (0.05) in a certain library helped me in being one of the first people to discover a floating-point technology flaw that Apple shipped in their Rosetta library during the PowerPC-to-Intel transition. Such a flaw reaching the public would have seemed impossible. But amazingly, the flaw had escaped the notice of the originating vendor, Transitive, and Apple, and the early-access developers testing on Apple’s betas.

    (By the way, I should mention… never use floating-point for money, use BigDecimal.)

    Choice of frameworks

    Rather than use the built-in assert facility, you may want to consider using another assertion framework. You have multiple options, including:

    • JUnit
      See: org.junit.jupiter.api.Assertions.
    • AssertJ
      Known for its slick fluent interface.
    • Hamcrest
      Used across many languages (Java, Python, Ruby, Swift, etc.).

    Or roll-your-own. Make a little class to use in your project. Something like this.

    package work.basil.example;
    
    public class Assertions {
        static public void assertTrue ( Boolean booleanExpression , CharSequence message ) throws java.lang.AssertionError {
            if ( booleanExpression ) {
                // No code needed here.
            } else { // If booleanExpression is false rather than expected true, throw assertion error.
                // FIXME: Add logging.
                throw new java.lang.AssertionError( message.toString() );
            }
        }
    
    }
    

    Example usage:

    Assertions.assertTrue( 
        localTime.isBefore( LocalTime.NOON ) , 
        "The time-of-day is too late, after noon: " + localTime + ". Message # 816a2a26-2b95-45fa-9b0a-5d10884d819d." 
    ) ;
    

    Your questions

    They arrived relatively late (Java 1.4), by which time many people had already established their Java programming style/habit

    Yes, this is quite true. Many people were disappointed by the API that Sun/JCP developed for assertion-testing. Its design was lackluster in comparison to existing libraries. So many ignored the new API, and stuck with known tools (3rd-party tools, or roll-your-own mini-library).

    They are turned off at runtime by default, WHY OH WHY??

    In the earliest years, Java got a bad rap for poor performance speed. Ironically, Java quickly evolved to become one of the best platforms for performance. But the bad rap hung around like a stinky odor. So Sun was extremely wary of anything that might in any measurable way impact performance. So in this perspective, it made sense to make disabling assertion-testing the default.

    Another reason to disable by default might have been related to the fact that, in adding the new assertion facility, Sun had hijacked the word assert. This was not a previously reserved keyword, and required one of the few changes ever made to the Java language. The method name assert had been used by many libraries and by many developers in their own code. For some discussion of this historical transition, read this old documentation, Programming With Assertions.

    0 讨论(0)
  • 2020-12-04 21:37

    In fact they arrived in Java 1.4.

    I think the main problem is that when you code in an environment where you do not manage JVM options directly by yourself like in Eclipse or J2EE servers (in both cases it is possible to change JVM options, but you need to deeply search to find where it can be done), it is easier (I mean it requires less effort) to use if and exceptions (or worse not to use anything).

    0 讨论(0)
提交回复
热议问题