Typo-checking instead of static typing

Hi,

So, what advice would you guys give to a C programmer who wants a Ruby tool
to look over lines of code he may rarely execute?

Go with Java and use Generics. Avoid reflection, bytecode engineering
and AOP. :slight_smile:

Cheers,
Joao

[ snip ]

def do_something(foo)
  if foo > 500
    puts "Whoa, #{fo} is too many times! I can't do that!"
  else
    foo.times { |i| puts i }
  end
end

do_something(ARGV[0])

[ snip ]

Then there's the typo in the "puts" line. Until the person happens to try
an arg greater than 500, they will have no indication the code has a flaw.

Now, would a unit test catch this? If the programmer was careful, maybe.
But maybe not.

So, what advice would you guys give to a C programmer who wants a Ruby tool
to look over lines of code he may rarely execute?

Use a code coverage tool to run over your unit tests, and fix your
tests to go to 100% coverage. That's what I usually try to do in Perl anyway.

Pointers to coverage tools for ruby anyone?

Joost.

···

On Thu, May 05, 2005 at 03:49:30AM +0900, Ben Giddings wrote:

Charles Mills wrote:

Hal Fulton wrote:

Eric Hodel wrote:
   

My open question is then: how do you find typos and brainos in
       

seldom-run

code in a dynamic language like Ruby?
       

Yes, unit tests and ruby -w. I wrote the thing in the 'I Rock.'
     

post

between midnight and 4am. As you can imagine, writing code tired
     

is

(flori@lambda:flori 0)$ gem list -r |grep covera
coverage (0.2, 0.1)

···

On 03 May 2005, at 12:27, Ben Giddings wrote:

fraught with crazy typos, but the unit tests found them all.

When I was almost done, I spent 15 minutes on a 1 character typo
     

(rrc in

one spot, rcc in another). I didn't have a unit test for that
     

case.

Let me put in a plug for code coverage tools in general.
Combined with good unit tests, these are truly invaluable.

There's a good one out there, but I forget what it's called --
<admission>I don't use it much, I've only played with it.</admission>

Hal
   
Are there any code coverage tools for Ruby?

-Charlie

--
Florian Frank

[lots of test result stuff snipped]
oops, all that was all prepped for xml, so replace the &gt; and &lt;
characters as needed :slight_smile:

···

On 5/3/05, pat eyler <pat.eyler@gmail.com> wrote:

unit_test is awesome! Here's an example from an article I wrote
recently (to be published soon) -- first without unit_test, then the
same thing with unit_test.

--
thanks,
-pate
-------------------------
We are often unable to tell people what they need to know, because
they want to know something else, and would therefore only
misunderstand what we said
- the Raven (George MacDonald, Lilith)

anyway.

What unit tests would you suggest for that bit of sample code, keeping in
mind that the original code is only 8 lines long.

Ben

···

On Wednesday 04 May 2005 15:36, Joost Diepenmaat wrote:

Use a code coverage tool to run over your unit tests, and fix your
tests to go to 100% coverage. That's what I usually try to do in Perl

[ snip ]

> def do_something(foo)
> if foo > 500
> puts "Whoa, #{fo} is too many times! I can't do that!"
> else
> foo.times { |i| puts i }
> end
> end
>
> do_something(ARGV[0])

Use a code coverage tool to run over your unit tests, and fix your
tests to go to 100% coverage. That's what I usually try to do in Perl

anyway.

Pointers to coverage tools for ruby anyone?

I sent an email reply to this, but nobody followed up, but I'm really
interested to hear the answer.

The suggestion here was to write unit tests to make sure that the line with
the typo is caught ("Whoa, #{fo} ..." instead of "Whoa, #{foo} ...")

My question is this. If the "Ruby Way" to find this bug is by unit tests,
what suite of tests would be recommended for this snippet of code? Keep
in mind, that the original code is only 8 lines long.

Is it realistic to expect that someone would write more than 8 lines of
unit tests?

Ben

···

On Wednesday 04 May 2005 15:36, Joost Diepenmaat wrote:

On Thu, May 05, 2005 at 03:49:30AM +0900, Ben Giddings wrote:

Ben Giddings wrote:

My question is this. If the "Ruby Way" to find this bug is by unit tests,
what suite of tests would be recommended for this snippet of code? Keep
in mind, that the original code is only 8 lines long.

Is it realistic to expect that someone would write more than 8 lines of
unit tests?

Absolutely. In fact, this should be the norm. When you consider edge cases
and a good sampling of non-edge cases, the unit test can quickly outgrow
the code being tested.

It may seem like a lot of work, but that work continues to pay off
everytime you run the tests, especially after code changes or
refactorings.

Ryan

[ snip ]

> def do_something(foo)
> if foo > 500
> puts "Whoa, #{fo} is too many times! I can't do that!"
> else
> foo.times { |i| puts i }
> end
> end
>
> do_something(ARGV[0])

Use a code coverage tool to run over your unit tests, and fix your
tests to go to 100% coverage. That's what I usually try to do in Perl

anyway.

Pointers to coverage tools for ruby anyone?

I sent an email reply to this, but nobody followed up, but I'm really
interested to hear the answer.

I have a feeling I am answering to the wrong person, but:

Did you try Ryan Davis' zentest[1]? It automatically generates
tests for an existing codebase and should even take into account
any existing tests. I have not used it extensively but it might be
a good starting point?

The suggestion here was to write unit tests to make sure that the line with
the typo is caught ("Whoa, #{fo} ..." instead of "Whoa, #{foo} ...")

My question is this. If the "Ruby Way" to find this bug is by unit tests,
what suite of tests would be recommended for this snippet of code? Keep
in mind, that the original code is only 8 lines long.

Is it realistic to expect that someone would write more than 8 lines of
unit tests?

Ben

E

[1] http://www.zenspider.com/ZSS/Products/ZenTest/

···

Le 6/5/2005, "Ben Giddings" <bg-rubytalk@infofiend.com> a écrit:

On Wednesday 04 May 2005 15:36, Joost Diepenmaat wrote:

On Thu, May 05, 2005 at 03:49:30AM +0900, Ben Giddings wrote:

--
template<typename duck>
void quack(duck& d) { d.quack(); }

Absolutely. In fact, this should be the norm. When you consider edge

cases

and a good sampling of non-edge cases, the unit test can quickly outgrow
the code being tested.

It may seem like a lot of work, but that work continues to pay off
everytime you run the tests, especially after code changes or
refactorings.

Ok, so in this case, what would the unit tests be?

Ben

···

On Friday 06 May 2005 15:09, Ryan Leavengood wrote:

Ben Giddings wrote:

Absolutely. In fact, this should be the norm. When you consider edge
cases and a good sampling of non-edge cases, the unit test can
quickly outgrow the code being tested.

It may seem like a lot of work, but that work continues to pay off
everytime you run the tests, especially after code changes or
refactorings.

Ok, so in this case, what would the unit tests be?

Ben

This one is rather tricky.
I assume your do_something is part of a Foo class.
I then do a hack to mock up puts to put it in a global

So your tests might look like this:

require 'test/unit'
require 'Foo'

class Foo
def puts(x)
   $x << x.to_s << "\n"
end end

class TestFoo < Test::Unit::TestCase
  def setup
    @f = Foo.new
    $x = ""
  end
   def test_do_something_0
   @f.do_something(0)
    assert_equal("",$x)
  end

  def test_do_something_3
    @f.do_something(3)
    assert_equal("0\n1\n2\n",$x)
end
   def test_do_something_501
    @f.do_something(501)
    assert_equal("Whoa, 501 is too many times! I can't do that!\n",$x)
  end
    def test_do_something_nil
    @f.do_something(nil)
    assert_equal("",$x)
  end
  end

There may be an easier way. *shrug*

···

On Friday 06 May 2005 15:09, Ryan Leavengood wrote:

--
J. Lambert

Jon A. Lambert wrote:

Ben Giddings wrote:

Ok, so in this case, what would the unit tests be?

Ben

This one is rather tricky.
I assume your do_something is part of a Foo class.
I then do a hack to mock up puts to put it in a global

So your tests might look like this:

[snip tests]

My feeling is that code like we are discussing should be refactored to be more easily unit tested. Some developers take issue with modifying code for the sake of testing, but myself, XPers and others who extensively use unit tests tend to notice improvements in the code by making it test friendly. Because of the nature of unit tests, code that is designed to be unit tested is cohesive and has low coupling, which are well known metrics of software quality.

Here is my quick attempt at refactoring the "do_something()" method to be more easily unit tested (as well as Jon Lambert's refactored unit tests):

require 'test/unit'

# Errors should be made into Exceptions, not just printed out
class FooOverloadException < RuntimeError; end

class Foo
   # Instead of just using puts, do_something should return a String,
   # which can be further manipulated, printed out, or sent over
   # the network.
   def do_something(foo)
     s = ''
     # Using the to_i method allows for nil (which returns 0 on to_i)
     if foo.to_i > 500
       raise FooOverloadException, "Whoa, #{foo} is too many times! I can't do that!"
     else
       foo.to_i.times { |i| s << "#{i}\n" }
     end
     s
   end
end

class TestFoo < Test::Unit::TestCase
   def setup
     @f = Foo.new
   end

   def test_do_something_0
     assert_equal("",@f.do_something(0))
   end

   def test_do_something_3
     assert_equal("0\n1\n2\n",@f.do_something(3))
   end

   def test_do_something_501
     exception = assert_raise(FooOverloadException) do
       @f.do_something(501)
     end
     assert_equal("Whoa, 501 is too many times! I can't do that!",exception.message)
   end

   def test_do_something_nil
     assert_equal("",@f.do_something(nil))
   end
end

Now doesn't that look better? :slight_smile:

Ryan