Forum: Ruby on Rails Idiom question - assertions which aren't in tests

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Paul B. (Guest)
on 2005-12-28 14:04
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 :-)

Thanks in advance for your help!

paul.butcher->msgCount++

Snetterton, Castle Combe, Cadwell Park...
Who says I have a one track mind?
Kent S. (Guest)
on 2005-12-28 19:03
(Received via mailing list)
What would be the result of your assert statement? If you just want
to log an event then you can just

unless <condition>
   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.
Paul B. (Guest)
on 2005-12-28 19:42
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 ;-)

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 :-)

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?
Reginald Braithwaite (Guest)
on 2005-12-29 18:26
(Received via mailing list)
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
Paul B. (Guest)
on 2005-12-29 18:52
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?
Dean W. (Guest)
on 2005-12-29 19:29
(Received via mailing list)
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
Reginald Braithwaite (Guest)
on 2005-12-29 21:12
(Received via mailing list)
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.
Stephen W. (Guest)
on 2005-12-29 22:06
(Received via mailing list)
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
Steve R. (Guest)
on 2006-01-18 09:29
(Received via mailing list)
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?
Paul B. (Guest)
on 2006-01-18 12:33
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?
This topic is locked and can not be replied to.