assert{ 2.0 } - a new kind of assertion

Discussion in 'Ruby' started by Phlip, Jan 29, 2008.

  1. Phlip

    Phlip Guest

    I have invented a new concept of developer test assertions. This post is a
    preview of its features, before I release it for Ruby. Porting it to other
    languages is left as an exercise for the reader.

    assert{ 2.0 }

    I don't like the simple assertions - assert_equal, assert_match,
    assert_not_nil, etc, in my developer tests. They only exist for
    one reason - to print out their values when they fail. And then
    they don't even reflect their variable names, either.

    So I wrote an assertion to replace them. Put whatever you want
    into it; it prints out your expression, and all its values.
    Essentially like this:

    x = 43
    assert{ x == 42 } --> x == 42
    x --> 43

    deny{ x == 43 } --> x == 43 should not pass
    x --> 43

    The classic versions require more typing, and reflect less information:

    assert_equal(x, 42) --> <43> expected but was \n<42>
    assert_not_equal(x, 43) --> <43> expected to be != to \n<43>

    This is a new concept of an assertion, and it simplifies the hell
    out of developer tests. Before:

    def test_attributes
    topics = create_topics
    assert_equal 'a topic', topics['first']
    assert_not_nil topics['second']
    end

    After:

    def test_attributes
    topics = create_topics
    assert{ 'a topic' == topics['first'] }
    assert{ topics['second'] }
    end

    If the first assert_equal failed, it would only print out the two values.

    When assert{} fails, it prints its complete expression, with each
    intermediate term and its value:

    assert{ "a topic" == ( topics["first"] ) } --> false
    topics --> {"first"=>"wrong topic"}
    topics["first"] --> "wrong topic"

    And if the assert_not_nil failed, it would only reward us with
    the infamous diagnostic "<nil> expected to not be nil". We would
    prefer to see the expression that failed, and its intermediate
    values!

    assert{ topics["second"] } --> nil - should not pass
    topics --> {"first"=>"wrong topic"}
    topics["second"] --> nil

    I'm still working on the library supporting this assertion.
    It uses 'rubynode' to read your block's raw nodes. We already use
    the assertion in all our projects at work, where it tends to
    simplify the excess code we must write in test cases.
     
    Phlip, Jan 29, 2008
    #1
    1. Advertising

  2. Phlip

    Jim Kingdon Guest

    > So I wrote an assertion to replace them. Put whatever you want
    > into it; it prints out your expression, and all its values.


    Nice.

    The one comment I have is that I generally write assertions with the
    assumption that the programmer will read the failure in conjunction
    with the code that failed (in many development environments, the code
    is a single click away, at least for java, not as sure about ruby).
    So that somewhat mitigates the need for this. But I could see this
    feature if the development environment doesn't make it that easy, or
    for some of the other features (I think the above-mentioned code will
    be more helpful on something like assert { x == y }, printing both x
    and y with names).
     
    Jim Kingdon, Jan 29, 2008
    #2
    1. Advertising

  3. Phlip

    Phlip Guest

    Jim Kingdon wrote:

    >(I think the above-mentioned code will
    > be more helpful on something like assert { x == y }, printing both x
    > and y with names).


    It can reflect anything. This frees you up to write whatever expressive
    statement you like between the {}, so long as it returns a meaningful polarity.
    (Use deny{} if that polarity is false.)

    Yes, some editors don't have good "Fault Navigation". The even better situation,
    at fault time, is preventing the need to navigate. You read the fault diagnosis,
    and then keep editing whatever code is already in your editor.

    I do Ruby on Rails under the equivalent of 'autotest', so frequent test runs
    make fault navigation less important. Upgrading our editor is on our do-list.

    --
    Phlip
     
    Phlip, Jan 29, 2008
    #3
  4. On Tuesday 29 January 2008 10:29, Phlip wrote:
    > I have invented a new concept of developer test assertions. This post is a
    > preview of its features, before I release it for Ruby. Porting it to other
    > languages is left as an exercise for the reader.
    >
    > assert{ 2.0 }
    >
    > I don't like the simple assertions - assert_equal, assert_match,
    > assert_not_nil, etc, in my developer tests. They only exist for
    > one reason - to print out their values when they fail. And then
    > they don't even reflect their variable names, either.
    >
    > So I wrote an assertion to replace them. Put whatever you want
    > into it; it prints out your expression, and all its values.
    > Essentially like this:
    >
    > x = 43
    > assert{ x == 42 } --> x == 42
    > x --> 43
    >
    > deny{ x == 43 } --> x == 43 should not pass
    > x --> 43


    Phlip,

    Very nice! I've used C's macro pre-processor to do something like
    that for some 20 years.

    #ifdef RIGOROUS
    #define ASSERT(ex) {if(!(ex))fprintf(stderr,"ex false in %s, line %d\n",__FILE__,__LINE__);}
    #else
    #define ASSERT(ex)
    #endif

    The default is NOT to check the assertion, since some take quite a bit
    of time. Here are some examples:
    ASSERT(-4712 <= year && year != 0)
    ASSERT(!(year == 1582 && month == 10 && 4 < day && day < 15))
    ASSERT(0 <= newStream->queueNextEmpty && newStream->queueNextEmpty < newStream->queueSize)
    ASSERT(streamp->queue[at - size])
    ASSERT(filename && *filename)
    ASSERT(compare(e1, e2) == result);
    ASSERT(0 <= linebufp-linebuf && linebufp-linebuf <= line_width)

    Here's comes from the last one when the assertion fails

    0 <= linebufp-linebuf && linebufp-linebuf <= line_width false in format.c, line 110

    -paul-
     
    Paul E. Black, Jan 29, 2008
    #4
  5. Phlip

    Phlip Guest

    > You mean like you can easily do in Common Lisp:

    Already pointed out on another forum.

    > Ha, ha, only serious,


    I'll stick with the language where every parenthesis pair is optional, thanks.
     
    Phlip, Jan 30, 2008
    #5
  6. On Wed, 30 Jan 2008 06:50:57 -0800, Phlip wrote:

    > I'll stick with the language where every parenthesis pair is optional, thanks.


    I always wondered why dynamically typed languages do not make brackets
    dynamic... To enjoy that at full one could invent assertions asserting that
    the numbers of left and right brackets are same... Or what about asserting
    statements:

    if assert I said, "if"! then assert I really mean "then"! or else! ...

    you get the idea... :)-))

    --
    Regards,
    Dmitry A. Kazakov
    http://www.dmitry-kazakov.de
     
    Dmitry A. Kazakov, Jan 30, 2008
    #6
  7. Phlip

    Jerry Coffin Guest

    In article <>, says...

    [ improved asserts for Ruby ]

    > You mean like you can easily do in Common Lisp:
    >
    > http://www.gigamonkeys.com/book/practical-building-a-unit-test-framework.html
    >
    > It's nice to see people making the effort to bring these primitive
    > languages into the brave new world of the late 1950s and early 1960s.
    > ;-)


    Your code depends heavily on macros, which didn't exist in Lisp until
    1963. Even so, the early macro facilities were fairly ugly, and I'm
    pretty sure would not have handled code even vaguely similar to yours.

    The first macro facilities that resemble what you use were introduced in
    the dialects of Lisp invented for the MIT Lisp Machine project in
    roughly the mid-1970's.

    That, however, was only when the language provided the facilities to
    implement your code -- I don't know of anybody having written such a
    thing until considerably later. Strangely, C had actually implemented a
    relatively complete assert facility around the time it became possible
    to do so in Lisp.

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
     
    Jerry Coffin, Jan 31, 2008
    #7
  8. Phlip

    Jerry Coffin Guest

    In article <>, mailbox@dmitry-
    kazakov.de says...

    [ ... ]

    > if assert I said, "if"! then assert I really mean "then"! or else! ...


    Fortran did it first:

    if if .eq. 1 then then = 2 else else = 3

    is perfectly legitimate, since key words aren't reserved. Nicely enough,
    there's no reason you can't embed a space character into a variable name
    either, so something like:

    do 10 i = 1.10

    is perfectly legal too. Change it to:

    do 10 i = 1,10

    and it's a loop -- but with the decimal point instead of a comma, it's a
    simple assignment of the value 1.10 to a variable (that didn't have to
    be declared either) named 'do 10 i'!

    Not to be outdone, the designers of PL/I decided that:

    x = y = 0;

    should be legitimate and allowable -- but after executing it, x
    absolutely would NOT equal y!

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
     
    Jerry Coffin, Jan 31, 2008
    #8
  9. Phlip

    Jerry Coffin Guest

    In article <47a154ae$0$4950$>, phlip2005
    @gmail.com says...
    > Jerry Coffin wrote:
    >
    > > Strangely, C had actually implemented a relatively complete assert
    > > facility around the time it became possible to do so in Lisp.

    >
    > Even the oldest C could do stringerization, with a trick:
    >
    > #define assert(foo) if(!(foo)) fprintf(STDERR, "\
    > foo
    > \ failed");


    I don't think the _very_ oldest C could handle this. If you ever feel
    like looking, Dennis Ritchie has a museum on his web site that includes
    some early C compilers. These include some versions so old some of the
    _very_ basic parts of the language were still in flux (e.g. on that
    doesn't support 'struct' at all). OTOH, but the mid-70's, things had
    settled down quite a bit -- in particular, memory was no longer quite so
    constrained, so he could implement a more complete language without
    simply overflowing the memory available on the (only) Unix machine at
    the time.

    > Someone check my syntax, but that would reflect 'whatever' if you failed
    > assert(whatever). However, this could not _reliably_ reflect argument
    > values. Even C++, with <iostream>, cannot reliably reflect both arguments
    > and values, for an assertion that's healthy to type.


    True -- I didn't mean to imply that its original assert was identical or
    equivalent to what you've written; quite the contrary, C (and C++) have
    make any sort of reflection quite difficult at best. OTOH, what they
    provided 30 years ago (or so) still seems to be better than what Ruby
    does by default...

    > So C++ made up for this by letting me put a breakpoint into the
    > assertion, so it would break directly into the calling code. I could TDD
    > in the debugger quite effectively like that.
    >
    > What my assertion gives for a Ruby project, without a debugger, is all
    > the "watch points" that a debugger would have provided:
    >
    > assert_{ reflect_string(statement) == statement } --> false -
    > should pass
    > statement --> "lambda{|*a| p( a ) }"
    > reflect_string(statement) --> "lambda{|*a| p(a) }".
    >
    > Yes, Lisp probably did, too, that before I was born!


    Without knowing how old you are, that's harder to guess. Then again,
    even if I did know your exact age, it could be hard to answer. Most
    languages draw a clear line between the compiler/interpreter/whatever
    and your code. Lisp doesn't; it allows you to play directly with the
    implementation quite easily, and it's generally considered quite
    reasonable for ordinary code to use things that would be completely
    hidden and inaccessible in most other languages. As such, it's usually
    difficult to say anything like "nobody did X before day Y", with respect
    to Lisp.

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
     
    Jerry Coffin, Jan 31, 2008
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?ISO-8859-1?Q?F=E1bio?= Mendes

    PEP new assert idiom

    =?ISO-8859-1?Q?F=E1bio?= Mendes, Nov 6, 2004, in forum: Python
    Replies:
    28
    Views:
    769
    Raymond Hettinger
    Nov 8, 2004
  2. Robert Brewer
    Replies:
    1
    Views:
    518
    bsmith
    Nov 7, 2004
  3. Thomas Guettler

    assert 0, "foo" vs. assert(0, "foo")

    Thomas Guettler, Feb 23, 2005, in forum: Python
    Replies:
    3
    Views:
    2,563
    Carl Banks
    Feb 23, 2005
  4. Alex Vinokur

    assert(x) and '#define ASSERT(x) assert(x)'

    Alex Vinokur, Nov 25, 2004, in forum: C Programming
    Replies:
    5
    Views:
    957
    Keith Thompson
    Nov 25, 2004
  5. ImpalerCore

    To assert or not to assert...

    ImpalerCore, Apr 27, 2010, in forum: C Programming
    Replies:
    79
    Views:
    1,753
    Richard Bos
    May 17, 2010
Loading...

Share This Page