From: Phlip on
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.
From: Jim Kingdon on
> 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).
From: Phlip on
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
From: Paul E. Black on
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-
From: Robert Klemme on
On 29.01.2008 19:02, Paul E. Black wrote:
> 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

My C is a bit rusty these days but isn't it safer to define the non
assertion case as

#defind ASSERT(ex) /* ex */

i.e. at least generate an empty comment? Otherwise you get only
whitespace and parsing of the source my change.

Kind regards

robert