Displaying articles with tag

State of rs tests

Posted by rue, Mon Oct 02 04:36:00 UTC 2006

As mentioned earlier, I was working on converting the rs tests from RSpec to chris2’s Test::Spec based on Test::Unit. The conversion went smoothly apart from some minor extra work in mocking (these should be resolved in the next release). I was able to clean up some things and actually fixed a minor bug—and more than one not-quite-perfect test where the original version could not confidently be said to be correct.

It also put the tests so far in perspective—with BDD, I seem to be more focused on the process of writing code via specifications—which I think I am still using nowhere near to their potential—which is rewarding; but it comes with the price of not testing for failure except for egregious cases. Generally, I am ensuring that the code works with expected input and the corner cases I have thought of but I am not going out of my way to hammer it with random data or known Bad Stuff™. This is definitely something that needs to be improved on in the future.

Some stats on the tests: the working directory I was using is missing some of the newest changes but it clocks in at around 330 lines of Ruby code for the rs libraries with slightly over 1500 lines of tests— an almost 5:1 ratio. There are 76 specifications and a total of 131 requirements (assertions, if you wish)—essentially one assertion for three lines of Ruby code. For your amusement, here is a logfile of a test run with the SpecDox runner.

0 comments | Filed Under: rs | Tags:

Test::Spec released

Posted by rue, Fri Sep 29 21:49:00 UTC 2006

Christian Neukirchen (chris2) just released Test::Spec, an implementation of BDD leveraging the existing standard library Test::Unit framework (RSpec is the canonical BDD framework but it is dependent on RubyGems and, to me, seems a bit overengineered). Christian was also kind—or lazy—enough to include my should_output implementation, too!

Get the most recent version from http://chneukirchen.org/releases.

The implementation is a very slick hundred-or-so lines but the real beauty of it is that it works essentially with just a require 'test/spec' at the top of the file; you can also freely intersperse old-style Test::Unit tests and assertions with the new ones.

Translation from RSpec is simple: Test::Spec uses the “Magick Dot” notation which means that my old obj.should_not_match other becomes obj.should.not.match other. Old Test::Unit tests need not be changed but if one wants to convert those, that change is a bit more profound and beyond the scope of this drivel—you can shamelessly raid the RSpec website or simply ask Unca Google (the test/ directory in the Test::Spec distribution also has self-tests to refer to).

Test::Spec, like Test::Unit, does not come with a mocking framework so you must select one of the existing ones—I am going with FlexMock. Mocha is apparently also good but I had a bad experience trying to mock/stub methods (i.e. it did not work so I gave up). It does seem that using FlexMock with Test::Spec means having to explicitly call #mock_verify on the mock objects which may have something to do with how Test::Unit normally handles its scopes (or may not, who knows).

For rs, the changes were fairly straightforward and I am doing the transition in two steps: for the first merge, I created $rs/test/vendor/ and stuck both FlexMock and Test::Spec in there to avoid depending (and having co-developers depend) on RubyGems and having to download additional software. I also revised the Rakefile slightly:
1
2
3
4
# This goes in the TestTask
t.libs      = [LIB, TEST, TEST_VENDOR]
t.options   = '-rs'              # Spec-style output
t.ruby_opts = ['-r test/spec' ] # Automatically loaded
That was it! With that change merged, I started writing all tests with Test::Spec (so there were actually three testing frameworks supported at the same time). For the second phase I will convert all old RSpec tests to Test::Spec—since that set is now static—and then merge them in at which point I can drop RSpec support completely.

/Edit: gave up on Mocha/

0 comments | Filed Under: | Tags:

Automated RSpec skeleton creation

Posted by rue, Sat Sep 09 05:56:00 UTC 2006

Note: this works just fine for Test::Spec as well.

I wrote a little helper script to create skeleton RSpec specification files. This way I can just write a .yaml file like this:
1
2
3
4
---
some context:
  - some specification
  - some other specification
The script creates the actual specs in proper format with a default failure for me. It even has a rudimentary way of adding specs to a context and appending to an existing spec file. Here you go:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/usr/bin/env ruby -w

require 'yaml'

# Output an entire context and all its specs
def write_contexts(contexts, out = $stdout)
  # Write the remaining new specs out
  contexts.each {|context, specifications|
    out.puts %{context "#{context}" do} 
    specifications.each {|spec|
      out.puts make_spec(spec)
    }                                 # specifications.each  
    out.puts "end"
    out.puts
  }                                   # data.each
end                                   # context

# Generate one empty spec
def make_spec(spec)
  %{  specify "#{spec}" do\n    fail '"#{spec}" not implemented'\n  end}
end                                   # specify


if __FILE__ == $0
  filename = ARGV.shift
  target   = ARGV.shift

  data = YAML.load File.read(filename)

  # Target file
  if target 
    old_data = File.readlines target if File.file? target

    # Overwrite the file interlacing
    File.open(target, 'w') {|f|
      (old_data || []).each {|line|
        # Write the data back
        f.puts line

        # Look for contexts 
        if line =~ /^\s*context\s+('|")(.*?[^\\])\1/
          # Check if we have new specs for this context
          if data[$2]
            # Inject the new specs here
            data[$2].each {|spec| f.puts make_spec(spec)}

            # Remove the specs from the equation
            data.delete $2
          end                           # if new specs  
        end                             # if context  
      }                                 # old_data.each 

      # Write the remaining contexts
      write_contexts data, f
    }                                   # open target

  # Direct output
  else
    # Print each context and specification section in turn
    write_contexts data
  end                                 # if target
end                                   # if __FILE__

0 comments | Filed Under: | Tags: