Idiom question - assertions which aren't in tests


#1

All,

Although I’ve been keeping an eye on Ruby for several years now, I don’t
have a huge amount of experience with it. So please forgive me if I’m
missing something obvious. I’m in the process of writing my first really
“serious” Rails app and would appreciate your advice.

I am wondering if there is a standard idiom for including assertions in
non-test code? By this, I mean something closer to the C++ concept of an
assertion (or Eiffel’s pre- and post-conditions) than the assertions
included within Test::Unit.

Note - I’m not talking about error handling here. I’m talking about
detecting bugs, but doing so when executing the code normally, not
within a test.

Clearly I can write something myself, but I’d rather not if I don’t have
to :slight_smile:

Thanks in advance for your help!

paul.butcher->msgCount++

Snetterton, Castle Combe, Cadwell Park…
Who says I have a one track mind?


#2

What would be the result of your assert statement? If you just want
to log an event then you can just

unless
logger.error “WARNING”
render(:file => “#{RAILS_ROOT}/public/500.html”,
:status => “500 Error”)
return
end

But I still believe that this code belongs to your unit tests. What I
usually do when I run in production mode, I catch all unhanded
exceptions and send myself an email with the information about
occurred exception by overriding
ActionController::Base#rescue_action_in_public method.

Kent.


#3

Kent S. wrote:

What would be the result of your assert statement? If you just want
to log an event then you can just

Actually the result isn’t really important - as long as it does
something “obvious” so that I know that there’s a problem.

Simplest would be to throw an exception. Maybe if I get really clever, I
could change the behaviour depending on whether we’re running in
development or production mode, but that’s not something I care about
right now.

I can clearly write something like your example snippet, but I would
prefer to write something simpler, more declarative and without the
duplication that scattering that snippet everywhere would result in.

But I still believe that this code belongs to your unit tests.

I’m certainly not arguing against unit tests. I want more though - the
opportunity to make assertions about the code during normal execution as
well as during testing (if a little paranoia is good, then lots of
paranoia is better :wink:

I’ll give you an example from the program that I’m writing right now. It
implements objects which can be locked, and which have a state
associated with them. If everything is working correctly, then objects
with the state “closed” should never be locked. I have, of course,
written a bunch of unit tests which check that this is always the case.
But I’m paranoid and would like to put something like this:

assert !(q.locked? && q.state == :closed)

at a few strategic locations within the code. Just in case there’s some
weird route which could end up with an invalid object I haven’t thought
of and covered in my unit tests. Of course if I do find such a route,
the first thing that I’ll do is write a test to cover it :slight_smile:

It’s looking like there’s nothing like this out there already (unless
anyone knows better?) - so I guess that I’ll write it myself.

My main problem is coming up with a name, given that “assert” is already
taken (an assert in non-test Rails code maps to Breakpoint#assert), and
so is “validate” (by ActiveRecord).

Suggestions gratefully received!

paul.butcher->msgCount++

Snetterton, Castle Combe, Cadwell Park…
Who says I have a one track mind?


#4

On 12/28/05, Paul B. removed_email_address@domain.invalid wrote:

Kent S. wrote:

What would be the result of your assert statement? If you just want
to log an event then you can just

Actually the result isn’t really important - as long as it does
something “obvious” so that I know that there’s a problem.

Why wouldn’t you write something like:

raise Foo.new(‘bah, humbug!’) unless something.is_obvious

Exceptions were expressly designed for errors and other ‘exceptional’
circumstances, right?


Reginald Braithwaite
http://www.braithwaite-lee.com/weblog/

Like all text messages, email exchanged with a gmail account may be
stored indefinitely and/or read by third parties without the sender or
receiver’s knowledge or permission. Please do not send any privileged
or confidential transmission to this account: enquire about secure
alternatives.

It’s a comment on the human condition that a shape so remarkable
should be contrived as a weapons system. But that is not the only
measure of the airplane’s worth. As an example of our advancing
ability to use computers to solve complex problems and our relentless
ability to innovate, the B-2 somehow seems worth having just because
it was so difficult to create.
–Larry Lowe


#5

Reginald Braithwaite wrote:

Why wouldn’t you write something like:

raise Foo.new(‘bah, humbug!’) unless something.is_obvious

In fact that’s exactly what I have been doing. But it’s making me uneasy
for these reasons:

Firstly, it’s wordier than

assert something.is_obvious

and therefore obscures the important thing that I’m trying to say (the
condition).

Secondly, the kind of thing that I’m checking is “impossible”. I’m not
talking about handling errors like disc full, or user entered invalid
information (which don’t indicate any kind of programming error). I’m
talking about detecting bugs in the code. Things which simply will never
happen unless I’ve screwed up. I would really like to have some way to
clearly distinguish the one from the other.

Finally, it would be nice to have the opportunity to either switch all
of these tests off, or change their behaviour, depending on whether I
was running in development or production mode.

Maybe all the years I’ve spent writing C++ code have corrupted me, but
this is exactly what assertions in C++ (and Java, for that matter) give
you, and I miss it.

paul.butcher->msgCount++

Snetterton, Castle Combe, Cadwell Park…
Who says I have a one track mind?


#6

This might be of interest:
http://ruby-contract.rubyforge.org/wiki/wiki.pl?HomePage

dean

On 12/28/05, Paul B. removed_email_address@domain.invalid wrote:

included within Test::Unit.
paul.butcher->msgCount++


Dean W.
http://www.aspectprogramming.com
http://www.newaspects.com
http://www.contract4j.org


#7

On 12/29/05, Paul B. removed_email_address@domain.invalid wrote:

Finally, it would be nice to have the opportunity to either switch all
of these tests off, or change their behaviour, depending on whether I
was running in development or production mode.

Ok, metaprogramming to the rescue. In your class method, you could
define your #assert method to have an empty body in production.

That way you don’t have to check production vs. develeopment mode
every time an asserion is checked.

The biggest problem will be evaluating the result. So your assert
method should take a block instead of an ordinary parameter. That way
you won’t evaluate condition in development either.

However, as you probably know from C++ and Java, you get a whole new
class of bugs if you accidentally rely on a side effect of an
expression within the assertion. In a perfect world you could
hygienically isolate expressions within the assertion to eliminate
side effects.


Reginald Braithwaite
http://www.braithwaite-lee.com/weblog/

“If a man is determined to be an Ass, someone is bound to ride him
like a Donkey.”
–John L.


#8

On Dec 29, 2005, at 11:09 AM, Reginald Braithwaite wrote:

In a perfect world you could
hygienically isolate expressions within the assertion to eliminate
side effects.

Maybe you could pull something like this off with continuations?

–Steve


#9

If you are a stickler for making Ruby “look” like C, how about:

def assert(condition)
# Use string so exception preserves the condition asserted
raise condition unless eval(condition)
end

some

code

here

assert('something < obvious')

etc.

Bear in mind that there is a perf hit each time you execute the eval, so
putting it in the code path is not without cost.

This is as blunt an instrument as assert() in C, but “impossible”
conditions
that you can foresee well enough to write an assert could also be
handled in
a rescue clause, no?


#10

Steve R. wrote:

If you are a stickler for making Ruby “look” like C, how about:

def assert(condition)

For the avoidance of doubt - I have absolutely no desire to make Ruby
“look” like C. I simply want to add extra checks for program
correctness.

This is as blunt an instrument as assert() in C, but “impossible”
conditions
that you can foresee well enough to write an assert could also be
handled in
a rescue clause, no?

I’m sorry - I don’t understand. How would rescue help?

I’m not interested in handling a problem - I’m interested in detecting
a problem.

paul.butcher->msgCount++

Snetterton, Castle Combe, Cadwell Park…
Who says I have a one track mind?