“TDD”.reverse

Stagger in disbelief if you’d like, but I’ve found an instance where it is more beneficial to not begin testing a piece of software until a measurable way through its development.

You no doubt remember my fabulous, in-development command-line NewsGator client, bulletin. I’ve extracted the NewsGator-specific code to a gem called WonderCroc. The README provides a small glimpse of what’s possible with the library now:

wondercroc

A gem that provides…

In any event, I haven’t written any tests for it yet. A lot of the initial programming was screwing around with Ruby’s RSS parser and running requests through in IRB. At this point, however, I’d like to start throwing specs at it as I refactor it and extend it to become a modular library.

So what?

Well, here’s the thing. I want to know that the library responds correctly to the XML responses sent by NewsGator’s server. I assume the correct way to test this is to mock the responses accurately. The NewsGator REST API documentation provides several partial samples, but they’re not complete or complex enough to give me a full range of parsing opportunities. So why not use actual responses?

So that’s my plan. Extend my WonderCroc::Client class to log the XML responses to a file, and use those in my mocks — something I can’t think of a good way to do if I hadn’t written the code in the first place.

Thoughts?

Ruby, RSpec and BDD Part I

Ruby, Tutorials — Tags: , , , — Ardekantur @ 7:10 pm

Here is a bit of work on Behavior Driven Development (BDD) with rspec in Ruby, based off the examples provided by this excellent Null Is Love series of posts. The only thing I know externally, in terms of BDD, is the process itself:

  1. Write a test for the functionality you want.
  2. Watch the test fail.
  3. Write code to support that functionality.
  4. Watch the test pass, or watch it fail, and fix your code.
  5. Repeat incrementally.

This post assumes that you’ve read and gone through the series of posts on Null Is Love. Gain an understanding of unit testing with those posts, and then come back here.

With that in mind, let’s start.

$ sudo gem install rspec -y
$ mkdir car
$ touch car/car_spec.rb car/car.rb

This is the gem we need, rspec, followed by the files we will be working with. So let’s start with the first leg of this - writing the functionality we want. Initially we will just want our Car object to initialize with the proper default values. We won’t be using all of the specification funcationality of rspec—just using it to write cleaner, english-language tests is enough for now.

car/car_spec.rb

require 'car'
 
describe Car do
 
  it "should initialize with the proper defaults" do
    default_car = Car.new
    default_car.model.should == 'Volvo'
    default_car.year.should  == 2007
    default_car.color.should == 'unknown'
  end 
 
end

The first thing you may notice is that this code is very clean, and extremely readable. It also does not try to over-extend itself. We are testing one small portion of our eventual code’s functionality.

Now we test our code.

$ spec -c car_spec.rb
./car_spec.rb:3: uninitialized constant Car (NameError)

Whoops! That’s right, we haven’t written any code yet. Our test will fail. This is what we want. If the tests passed immediately, we wouldn’t need to write them at all. This is a rather roundabout way of thinking about what we are doing, but it works. (NOTE: I am using the ‘-c’ parameter with spec in order to see colored output. This may or may not work on whatever terminal or terminal emulator you are using, but it is a wonderful way to feel better about your code when you start the green that lets you know your tests have passed.)

Let us start writing our code, then.

car/car.rb

class Car
  attr_accessor :model, :year, :color
 
  def initialize( options = {} )
    self.model = options[:model] || 'Volvo'
    self.year  = options[:year]  || 2007
    self.color = options[:color] || 'unknown'
  end
 
end

And now:

$ spec -c car_spec.rb 
.

Finished in 0.016665 seconds

1 example, 0 failures

Beautiful! We’re green.

Here are some other tests that reflect the concerns of the original design. Each of them requires no changes to the object code in order to pass.

  it "should use values provided as parameters to the constructor" do                                       
    custom_car = Car.new :model => 'Toyota', :year => 2005, :color => 'white'                               
    custom_car.model.should == 'Toyota'                                                                     
    custom_car.year.should  == 2005                                                                         
    custom_car.color.should == 'white'                                                                      
  end                                                                                                       
 
  it "should accept constructor parameters in any order" do                                                 
    shuffled_car = Car.new :color => 'red', :model => 'Kia', :year => 2008                                  
    shuffled_car.model.should == 'Kia'                                                                      
    shuffled_car.year.should  == 2008
    shuffled_car.color.should == 'red'
  end

Remember the part of Null is Love’s tutorial where we asked ourselves what the behavior of a certain accessor—namely, the year—should be?

Do you change your test or change your code? If you need to rely on string_car.year always being an integer, then I would suggest changing your code. If you want string_car.year to be able to accept either a string or an integer, then you change your test assertion so that “a string in results in a string out”. In most cases, it will probably the former.

With BDD, that question is asked, but in a different way. As we spec out our code, we specify what the behavior should be before we write the code that deals with it. In this case, the code will be changed to reflect the behavior we want, but that requirement is a sort of first class citizen, instead of an afterthought of going through the code, checking for gotchas.

car/car_spec.rb

    # ...
 
    it "should ensure years are always integers" do                                                           
      old_car = Car.new :year => '1960'                                                                       
      old_car.year.should == 1960                                                                             
    end
 
    #...

And the results of our test?

$ spec -c car_spec.rb 
...F

1)
'Car should ensure years are always integers' FAILED
expected: 1960,
     got: "1960" (using ==)
./car_spec.rb:28:

Finished in 0.017983 seconds

4 examples, 1 failure

Perfect! Now we write the code that lets our test pass—namely, converting the options[:year] parameter to an integer when it is passed.

I’ll continue this tutorial eventually, along with some additional information about heckling our code.

Merb and its Generators

Merb, Ruby, Software Design — Tags: , , , — Ardekantur @ 6:41 am

It’s slowly starting to appear that what Merb needs is to test the correctness of its generators, to ensure that no exceptions are raised during the generation process, as well as to ensure that all the files they create are accurately named and contain allowed Ruby syntax. How on earth you’d test this sort of thing is beyond me at the moment because I don’t fully understand how RubiGen works, or if that sort of testing should be delegated to RubiGen itself. Whatever the case, something needs to change.

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.
(c) 2008 Ardekantur | powered by WordPress with Barecity