Thursday, February 28, 2008

Ruby: Replace method_missing with dynamic method definitions

You have methods you want to handle dynamically without the pain of debugging method_missing.

class Decorator
def initialize(subject)
@subject = subject
end

def method_missing(sym, *args, &block)
@subject.send sym, *args, &block
end
end

becomes

class Decorator
def initialize(subject)
subject.public_methods(false).each do |meth|
(class << self; self; end).class_eval do
define_method meth do |*args|
subject.send meth, *args
end
end
end
end
end

Motivation

Debugging classes that use method_missing can often be painful. At best you often get a NoMethodError on an object that you didn't expect, and at worst you get stack level too deep (SystemStackError).

There are times that method_missing is required. If the usage of an object is unknown, but must support unexpected method calls you may not be able to avoid the use of method_missing.

However, often you know how an object will be used and using Dynamically Define Method you can achieve the same behavior without relying on method_missing.

MechanicsExample: Dynamic delegation without method_missing

Delegation is a common task while developing software. Delegation can be handled explicitly by defining methods yourself or by utilizing something from the Ruby Standard Library such as Forwardable. Using these techniques gives you control over what methods you want to delegate to the subject object; however, sometimes you want to delegate all methods without specifying them. Ruby's Standard Library also provides this capability with the delegate library, but we'll assume we need to implement our own for this example.

The simple way to handle delegation (ignoring the fact that you would want to undefine all the standard methods a class gets by default) is to use method_missing to pass any method calls straight to the subject.

class Decorator
def initialize(subject)
@subject = subject
end

def method_missing(sym, *args, &block)
@subject.send sym, *args, &block
end
end

This solution does work, but it can be problematic when mistakes are made. For example, calling a method that does not exist on the subject will result in the subject raising a NoMethodError. Since the method call is being called on the decorator, but the subject is raising the error it may be painful to track down where the problem resides.

The wrong object raising a NoMethodError is significantly better than the dreaded stack level too deep (SystemStackError). This can be caused by something as simple as forgetting to use the subject instance variable and trying to use a non-existent subject method or any misspelled method. When this happens the only feedback you have is that something went wrong, but Ruby isn't sure exactly what it was.

These problems can be avoided entirely by using the available data to dynamically define methods at run time. The following example defines an instance method on the decorator for each public method of the subject.

class Decorator
def initialize(subject)
subject.public_methods(false).each do |meth|
(class << self; self; end).class_eval do
define_method meth do |*args|
subject.send meth, *args
end
end
end
end
end

Using this technique any invalid method calls will be correctly reported as NoMethodErrors on the decorator. Additionally, there's no method_missing definition, which should help avoid the stack level too deep problem entirely.

Example: Using user defined data to define methods

Often you can use the information from a class definition to define methods instead of relying on method_missing. For example, the following code relies on method_missing to determine if any of the attributes are nil.

class Person
attr_accessor :name, :age

def method_missing(sym, *args, &block)
empty?(sym.to_s.sub(/^empty_/,"").chomp("?"))
end

def empty?(sym)
self.send(sym).nil?
end
end

The code works, but it suffers from the same debugging issues that the previous example does. Utilizing Dynamically Define Method the issue can be avoided by defining the attributes and creating the empty_attribute? methods at the same time.

class Person
def self.attrs_with_empty_methods(*args)
attr_accessor *args

args.each do |attribute|
define_method "empty_#{attribute}?" do
self.send(attribute).nil?
end
end
end

attrs_with_empty_methods :name, :age
end

Labels: , , , ,




Monday, February 25, 2008

Ruby: Dynamically Define Method

Defining Methods Dynamically

You have methods that can be defined more concisely if defined dynamically.

def failure
self.state = :failure
end

def error
self.state = :error
end

becomes

def_each :failure, :error do |method_name|
self.state = method_name
end

Motivation

I use Dynamically Define Method quite frequently. Of course, I default to defining methods explicitly, but at the point when duplication begins to appear I quickly move to the dynamic definitions.

Dynamically defined methods can help guard against method definition mistakes, since adding another method usually means adding one more argument; however, this is not the primary reason for Dynamically Define Method.

The primary goal for Dynamically Define Method is to more concisely express the method definition in a readable and maintainable format.

MechanicsExample: Using def_each do define similar methods.

Defining several similar methods is verbose and often unnecessary. For example, each of the following methods is simply changing the value of the instance variable state.

def failure
self.state = :failure
end

def error
self.state = :error
end

def success
self.state = :success
end

The above code executes perfectly well, but it's too similar to justify 11 lines in our source file. The following example could be a first step to removing the duplication.

[:failure, :error, :success].each do |method|
define_method method do
self.state = method
end
end

Dynamically defining methods in a loop creates a more concise definition, but it's not a particularly readable one. To address this issue I define the def_each method. The motivation for defining a def_each method is that it is easy to notice and understand while scanning a source file.

class Class
def def_each(*method_names, &block)
method_names.each do |method_name|
define_method method_name do
instance_exec method_name, &block
end
end
end
end
The instance_exec method

Ruby 1.9 includes instance_exec by default; however, Ruby 1.8 has no such feature. To address this limitation I generally include the following code created by Mauricio Fernandez.

class Object
module InstanceExecHelper; end
include InstanceExecHelper
def instance_exec(*args, &block)
begin
old_critical, Thread.critical = Thread.critical, true
n = 0
n += 1 while respond_to?(mname="__instance_exec#{n}")
InstanceExecHelper.module_eval{ define_method(mname, &block) }
ensure
Thread.critical = old_critical
end
begin
ret = send(mname, *args)
ensure
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
end
ret
end
end
With def_each now available I can define the methods similar to the example below.

def_each :failure, :error, :success do |method_name|
self.state = method_name
end

Example: Defining instance methods with a class method.

The def_each method is a great tool for defining several similar methods, but often the similar methods represent a concept that can be used within code to make the code itself more descriptive.

For example, the previous method definitions were all about setting the state of the class. Instead of using def_each you could define a states class method that would generate the state setting methods. Defining a states class method helps create more expressive code.

def error
self.state = :error
end

def failure
self.state = :failure
end

def success
self.state = :success
end

becomes

class Post
def self.states(*args)
args.each do |arg|
define_method arg do
self.state = arg
end
end
end

states :failure, :error, :success
end

Example: Defining methods by extending a dynamically defined module

Sometimes you have an object and you simply want to delegate method calls to another object. For example, you might want your object to decorate a hash so that you can get values by calling methods that match keys of that hash.

As long as you know what keys to expect, you could define the decorator explicitly.

class PostData
def initialize(post_data)
@post_data = post_data
end

def params
post_data[:params]
end

def session
post_data[:session]
end
end

While this works, it's truly unnecessary in Ruby. Additionally, it's a headache if you want to add new delegation methods. You could define method_missing to delegate directly to the hash, but I find debugging method_missing problematic and avoid it when possible. I'm going to skip straight to defining the methods dynamically from the keys of the hash. Let's also assume that the PostData instances can be passed different hashes, thus we'll need to define the methods on individual instances of PostData instead of defining the methods on the class itself.

class PostData
def initialize(post_data)
(class << self; self; end).class_eval do
post_data.each_pair do |key, value|
define_method key.to_sym do
value
end
end
end
end
end

The above code works perfectly well, but it suffers from readability pain. In cases like these I like to take a step back and look at what I'm trying to accomplish.

What I'm looking for is the keys of the hash to become methods and the values of the hash be returned by those respective methods. The two ways to add methods to an instance are to define methods on the metaclass and to extend a module.

Luckily for me, Ruby allows me to define anonymous modules. I have a hash and a decorator, but what I want is a way to define methods of the decorator by extending a hash, so I simply need to convert the hash to a module.

The following code converts a hash to a module with a method for each key that returns the associated value.

class Hash
def to_mod
hash = self
Module.new do
hash.each_pair do |key, value|
define_method key do
value
end
end
end
end
end

With the above code in place, it's possible to define the PostData class like the example below.

class PostData
def initialize(post_data)
self.extend post_data.to_mod
end
end

Labels: , ,




Sunday, February 24, 2008

Pair Programming all the time

I was having a discussion with a friend a few days ago about Pair Programming. I am, obviously, an advocate, but my friend doesn't believe in it.

Many people do not believe in Pair Programming (pairing). Most of those people don't agree with the benefits of pairing. However, my friend does believe that pairing is beneficial, he just does not believe in pairing all the time.

This is where semantics become very important. He was defining all the time as every minute that you are at work. I define all the time (in terms of pairing) as when I'm writing code that I'm going to commit.

There's plenty of time in the day that I spend on my own. Here's a few examples, but it's not representative of everything I do solo.Each of the above tasks I often take on without a pair. Usually I end up asking for help while I'm working alone, or I ask for someone to review my conclusions, but I don't force myself to pair when I'm not sure what direction I'm headed in.

Patrick Kua talks about working in two different modes, Experimental and Focused, in the context of Test Driven Development. I agree with Patrick and I believe the discussion also directly applies to Pair Programming. When you know what you want to get done, pairing ensures that you do it well. But, when you don't know what you need to do, it's generally a pain to try to talk through the problem with your pair. Talking through a problem can be helpful, but I often like to get a decent understanding before trying to explain it to someone else. I also like to be able to stop going in one direction and immediately switch to another if I spot something interesting.

Pair programming is a good thing, but like all good things, it's best when done in moderation. There are times when you do not need to pair, and Wikipedia has a decent list of reasons that people are opposed to pair programming. But, those things should not discourage you from giving Pair Programming a fair shot.

Given the right set up and approach, I believe pairing is the most efficient way to deliver quality software.

Labels:




Ruby: Inline Rescue

On my current project we use action specific Presenters 20% of the time. We opened ActionController and ActionView and redefined the render methods to automatically create presenters based on convention. For example the CommentsController has a show action, so render will try to create Comments::ShowPresenter when the show action is rendered. This works well for us, but like I said we only need a specific presenter 20% of the time. The other 80% of the time we still want a presenter that contains basic methods necessary on each page, but it is not specific to an action.

We solved this issue by attempting to create the specific presenter and rescuing with the base presenter.

klass = "#{action}_presenter".camelize.constantize rescue Presenter
@presenter = klass.new

Inline rescues promote concise code, but they shouldn't be overused to create overly complex lines of code. For example, I probably could have combined the two lines above into one line, but that would be a bit much to digest in my opinion.

In the example we attempt to create the class using constantize, but if an error (like uninitialized constant) occurs we rescue with the base Presenter class. Either way, the result is assigned to the klass local variable.

Labels:




Ruby: Creating Anonymous Classes

Classes in Ruby are first-class objects—each is an instance of class Class. -- ruby-doc.org
Most classes are defined using the syntax of the following example.

class Person
...
end

Defining classes this way is familiar to most developers. Ruby provides another syntax for defining classes using Class.new. The following example also defines the Person class.

Person = Class.new do
...
end
Class.new(super_class=Object) creates a new anonymous (unnamed) class with the given superclass (or Object if no parameter is given). You can give a class a name by assigning the class object to a constant. -- ruby-doc.org
The following example shows that the new class begins as an anonymous Class instance, but becomes a named class when it's assigned to a constant.

klass = Class.new
klass.ancestors # => [#<Class:0x21b38>, Object, Kernel]
klass.new.class # => #<Class:0x21b38>

Person = klass
Person.ancestors # => [Person, Object, Kernel]
Person.new.class # => Person

klass.ancestors # => [Person, Object, Kernel]
klass.new.class # => Person
klass # => Person

I generally use traditional syntax when defining named classes, but I have found anonymous classes very helpful when creating maintainable tests. For example, I often need to test instance methods of a Module. The following tests show how you can define a class, include a module, and assert the expected value all within one test method.

require 'test/unit'

module Gateway
def post(arg)
# implementation...
true
end
end

class GatewayTest < Test::Unit::TestCase
def test_post_returns_true
klass = Class.new do
include Gateway
end
assert_equal true, klass.new.post(1)
end
end

There are other ways to solve this issue, but I prefer this solution because it keeps everything necessary for the test within the test itself.

Another advantage to defining classes using Class.new is that you can pass a block to the new method; therefore, the block could access variables defined within the same scope. In practice, I've never needed this feature, but it's worth noting.

Labels: , ,




Tuesday, February 19, 2008

Rake: Task Overwriting

Update at bottom

By default Rake Tasks append behavior every time they are defined. The following example shows that both definitions are executed.

require 'rubygems'
require 'rake'

task :the_task do
p "one"
end

task :the_task do
p "two"
end

Rake::Task[:the_task].invoke
# >> "one"
# >> "two"

I like this behavior, but sometimes you want to overwrite a task instead of appending to what's already there.

When you no longer want the existing behavior the overwrite method can come in handy.

require 'rubygems'
require 'rake'

class Rake::Task
def overwrite(&block)
@actions.clear
enhance(&block)
end
end

task :the_task do
p "one"
end

Rake::Task[:the_task].overwrite do
p "two"
end


Rake::Task[:the_task].invoke
# >> "two"

The overwrite method is good, but sometimes you want to redefine the task using one of the specialized Rake tasks. I recently wanted to redefine the test task, so I created the abandon method to remove the existing definition.

require 'rubygems'
require 'rake'
require 'rake/testtask'

class Rake::Task
def abandon
@actions.clear
end
end

task :the_task do
p "one"
end

Rake::Task[:the_task].abandon

Rake::TestTask.new(:the_task) do |t|
t.libs << "test"
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end

Rake::Task[:the_task].invoke
# >> Expectations ..................
# >> Finished in 0.00442 seconds
# >>
# >> Success: 18 fulfilled

Hopefully you wont need this type of thing very often, but it can be handy when you want to overwrite a task that has been previously by a framework you've included.

Update below...
Ola Bini pointed out that I'm not clearing the prerequisites. Clearing the prerequisites is something you can do without any modifications to rake.

Rake::Task[:task_name].prerequisites.clear

Also, if you want prerequisites cleared as part of overwrite or abandon, you can easily add it to both tasks.

class Rake::Task
def overwrite(&block)
@actions.clear
prerequisites.clear
enhance(&block)
end
def abandon
prerequisites.clear
@actions.clear
end
end

Labels:




Sunday, February 17, 2008

Shared Ruby Code

It's no secret that I think plugins are unnecessary. Unfortunately, they are also popular, largely due to to how easy it is to create a plugin and put it in your Rails project.

It's also easy to create a new gem. In 2006 Dr. Nic released the newgem gem. This was a great step in 2006, but it's 2008 now and it doesn't feel like we've moved much in the way of shared ruby code.

We're all to blame.

The vast majority of Ruby work is working with Rails. I do believe the next step for RubyGems is to play seamlessly with Rails. Since RubyGems are great for any Ruby code, it doesn't make sense to add Rails specifics to RubyGems. However, RubyGems could be more opinionated about how gems are structured. Right now, RubyGems library code can be structured in almost any way that you'd like. If the structure followed a stricter convention then auto gem loading code could be written for Rails.

I like newgem, but I think the features provided by newgem should actually be part of RubyGems itself. If the gem command could create a project skeleton it could be opinionated about the file structure without creating a larger barrier to entry.

There is another change that needs to happen. Everyone knows that you should vendor everything for your Rails projects. But, there's no single step to go from uninstalled gem to vendored gem. The path is generally to install it locally and them unpack it in whatever folder you've created that gets autoloaded.

I'm pretty sure a remote unpack is fairly simple, which is why we are all to blame. RubyGems is an open source project. Anyone can contribute patches to make it better.

Lastly, if gems become the standard way to share Ruby/Rails code then we'll need a few rake tasks added to Rails. For example, who wouldn't want a rake command to upgrade to a later version of a gem.

I'm hoping 2008 is the year that shared ruby code becomes synonymous with RubyGems and the year Rails Plugins become unnecessary.

Labels: , ,




Friday, February 15, 2008

Implementing an internal DSL in Ruby

On Christmas Day 2007 I released the 0.0.1 version of expectations. Expectations is the result of several months of designing what I wanted the Domain Specific Language (DSL) to look like, and a few days of making it executable. To design the DSL, I created what I was looking for without worrying about implementation. This entry is going to focus on what it took to take the DSL from looking good to actually executing.

The following code is an example of what I decided I wanted the DSL to look like (for a state based test, expectations also has support for behavior based testing).

Expectations do
expect :expected do
:expected
end
end

When making a DSL executable I generally begin by building up objects with the values expressed in the DSL. For example, the above code creates a test suite containing 1 expectation where the expected value is :expected and the actual value can be determined by evaluating the block. While designing expectations I tested it using expectations, but that's probably more complex than is necessary for this example, so I'll simply print values instead.

The first step is ensuring that an Expectations::Suite is created when Expectations is called with a block. The following code demonstrates that an Expectations::Suite is created successfully. The code executes since the block given to Expectations is never executed.

require 'singleton'
module Expectations; end

class Expectations::Suite
include Singleton
end

def Expectations(&block)
Expectations::Suite.instance
end

Expectations do
expect :expected do
:expected
end
end

Expectations::Suite.instance # => #<Expectations::Suite:0x1d4fc>

note: Expectations::Suite is a singleton so that it can be appended to multiple times and later used for execution without being stored within another object.

The next step is to verify the number of expectations the suite stores for later execution. The following code verifies that one object is stored in the expectations array.

require 'singleton'
module Expectations; end

class Expectations::Suite
include Singleton
attr_accessor :expectations

def initialize
self.expectations = []
end

def expect(expected, &actual)
expectations << :expectation
end

end

def Expectations(&block)
Expectations::Suite.instance.instance_eval(&block)
end

Expectations do
expect :expected do
:expected
end
end

Expectations::Suite.instance # => #<Expectations::Suite:0x1c82c @expectations=[:expectation]>
Expectations::Suite.instance.expectations.size # => 1

The final step is to create an object for each expectation and verify the values of both the expected and the actual.

require 'singleton'
module Expectations; end

class Expectations::Suite
include Singleton
attr_accessor :expectations

def initialize
self.expectations = []
end

def expect(expected, &actual)
expectations << Expectations::Expectation.new(expected, actual)
end

end

class Expectations::Expectation
attr_accessor :expected, :actual

def initialize(expected, actual)
self.expected, self.actual = expected, actual.call
end
end

def Expectations(&block)
Expectations::Suite.instance.instance_eval(&block)
end

Expectations do
expect :expected do
:expected
end
end

Expectations::Suite.instance # => #<Expectations::Suite:0x1b184 @expectations=[#<Expectations::Expectation:0x1b0e4 @expected=:expected, @actual=:expected>]>
Expectations::Suite.instance.expectations.size # => 1
Expectations::Suite.instance.expectations.first.expected # => :expected
Expectations::Suite.instance.expectations.first.actual # => :expected

note: expectations actually defers determining the value of actual until the suite is executed, but that's outside the scope of this example.

The bottom two lines of the last example show that the Expectations::Expectation instance is being populated successfully.

That's it, at this point the DSL can successfully be parsed. Of course, making the expectations run to verify that the values match still needs to be done, but that's no different than working with any plain old ruby code. The DSL can be evaluated and the framework can work with the objects that have been created.

As the example demonstrates, designing an internal DSL in Ruby is very easy largely due to open classes, class methods, and the ability to eval. The above code for evaluating the DSL is less than 30 lines of code.

Labels: , ,




Thursday, February 14, 2008

Pair Programming thoughts

It's very rare that I go to a client and pair programming is not an issue. Developers love their desks, personal keyboard configurations, and music. I like that stuff too; unfortunately, any personal space benefits or keyboard configurations cannot make you more productive than a team of effectively pairing developers of the same skill level. I can't prove that, but you can't prove that I'm wrong either. Of course, to begin (a futile) attempt to prove which is more effective we would need to decide on what we were measuring. Building software isn't only about the number of features delivered, it's also about the quality of the software delivered. It's also about having a team that can maintain an application as (inevitable) turn over occurs.

That's what's so great about Pair Programming, it improves the quality of software while spreading knowledge and best practices at the same time. Everyone benefits from the sharing of knowledge from small things such as efficiency gaining key short cuts to large things such as understanding the big picture of the project and spreading common patterns throughout the system.

Adopting pair programming isn't as hard as you might think. In May of 2005 I wrote that setup is important. I was wrong, setup is not important, it's the most important step towards adoption. Anyone looking to bring pair programming into their organization doesn't need to do much. Get tables large enough for several team members to sit in the same area comfortably. Get the latest and greatest desktops for the pairing area. (My current client got me a beautiful Mac Pro). Get a desktop with dual video out if possible, or get a video splitter. Buy 2 keyboards, 2 mice, and 2 (large) monitors for each pairing station. If the pairing stations are the best systems to work on, developers will naturally migrate to those machines.

70% of ensuring adoption success is work station set up.

I suggest not mandating pairing, let it happen naturally. I also suggest allowing the developers to keep their desks. Everyone needs an area to escape to and read slashdot when they need a break. If you just bought everyone 30 inch monitors you may need to move those to the pairing stations and give everyone small monitors for their personal desks. The idea is to allow people to have their space, but encourage them to use the pairing stations by providing a superior set up.

20% of ensuring adoption is ping pong pair programming.

Ping pong pair programming is fantastic at keeping everyone involved and given the opportunity to drive.

The remaining 10% will vary by organization, but if you handle the first 90% the rest should just fall into place.

Labels:




Tests reflect code quality

As a consultant for ThoughtWorks I get to see a lot of different codebases. A pattern that I've seen in almost every codebase is that you can usually tell how well a codebase is written by the tests that cover the codebase.

The first indicator of quality is how covered the code is. In my experience the coverage percentage is generally representative of the confidence the business has in the software. Unfortunately, a low coverage percentage can show probable lack of quality, but a high coverage percentage does not ensure high quality. If you have less than 80% coverage, you probably have software that is broken in places. However, if you have better than 80% coverage it only means that you have better than 80% coverage. Again, coverage does not ensure quality.

The second indicator of quality is how easily new tests can be added to the existing suite. Working with someone for 10 minutes is usually enough time to see how much effort is required to add new tests (and features). If the developers believe in TDD, but are consistently frustrated with the level of effort it takes to write a test, you probably have a problem. Of course, if the developers do not believe in TDD, raising the barrier to entry with code that is hard to test only compounds the problem.

New tests can also be fairly easy to write, but very hard for new team members to understand. For example, special test superclasses designed to minimize the pain are also warning signs that the system may be unnecessarily complex.

Every project has a ratio of lines of application code and lines of test code. This ratio is effected by the language decision. The ratio does not need to be 1:1. In fact, language isn't the only factor in determining what the correct ratio is. For example, an internal application for a cafe that is not mission critical should not be as thoroughly tested as the command system for NASA's shuttles. The point is not to set some language standard ratio, but every project should recognize that if their desired ratio is 1:4 and it takes 8 times longer to write the tests than it takes to write the feature, something is wrong. The time to implement a feature versus the time to write the tests should be roughly equivalent to the application loc to test loc ratio.

While you do not need to strive for a 1:1 ratio, the farther you stray from 1:1 the more you should be looking at the reason for doing so. If your language requires a larger number, so be it. If your project demands more than the normal amount of tests, fair enough. But, you should always be on the look out for your ratio growing unjustifiably. It's always good to have at least one skeptic who believes that 1:1 is what you should be striving for. If you need 8+ lines of setup code to test a one line method you have a problem, and it pays to have a guy who is not afraid to point that out.

Conversely, I've rarely found a codebase that was easy to test that wasn't well written. I believe this is true largely because systems that are easily testable are generally very loosely coupled. Generally, loosely coupled applications are written fairly well to begin with. However, even if a component is poorly written, it will be easy to refactor or rewrite since it can be done independently of the rest of the system.

Along the same lines, when people ask me how to improve a legacy, monster codebase the first thing I recommend is breaking as many dependencies as possible. Once an application is broken into components it's possible to make educated decisions on what to refactor and what to redo from scratch.

Getting tests around a legacy codebase is always painful; however, it can also give you direction on where the application can be logically broken up. While writing tests for a legacy codebase you should keep track of dependencies that need to be setup, mocked or stubbed but have nothing to do with the current functionality you are focusing on. In general, these are the pieces that should be broken out into components that are easily stubbed (ideally in 1 or 0 lines).

Well written tests generally require loosely coupled, single responsibility code. The same type of code generally increases the quality of the codebase as a whole. Thus, tests that are readable and maintainable generally ensure the same type of codebase.

Labels:




Tuesday, February 12, 2008

Testing: Expect literals

When writing a state based test you must specify an expected value and an actual value. Whether you are using RSpec, Test::Unit, or any other framework, there is always an expected and an actual value.

# RSpec
presenter.hotel.should == :the_hotel

# Test::Unit
assert_equal :the_hotel, presenter.hotel

Expected values (:the_hotel in our example) can be any object; however, using literals (strings, integers, symbols) for expected values is advantageous for readability and traceability. To be clear, the expected value should be the literal itself, not a variable holding a literal that was created earlier in the test.

Tests that use literals are easier to read because they allow you to focus on the actual value, since the expected value is simply the literal. The following 3 tests verify the same thing, but the 3rd example uses a literal for the expected value. Since the 3rd example uses a literal, it's easy to see what the value of the actual should be without scanning the body of the test.

require 'test/unit'
require 'rubygems'
require 'expectations'

class SuiteTests < Test::Unit::TestCase
EXPECTED = :expected

def test_expectations_for_should_only_run_the_test_on_the_specified_line_with_local_var
expected = :expected
suite = Expectations::Suite.new
suite.expect(1) { 2 }
suite.expect(expected) { 2 }
assert_equal expected, suite.expectations_for(__LINE__ - 1).first.expected
end

def test_expectations_for_should_only_run_the_test_on_the_specified_line_with_constant
suite = Expectations::Suite.new
suite.expect(1) { 2 }
suite.expect(EXPECTED) { 2 }
assert_equal EXPECTED, suite.expectations_for(__LINE__ - 1).first.expected
end

def test_expectations_for_should_only_run_the_test_on_the_specified_line_with_literal
suite = Expectations::Suite.new
suite.expect(1) { 2 }
suite.expect(:expected) { 2 }
assert_equal :expected, suite.expectations_for(__LINE__ - 1).first.expected
end
end

note: these tests are fairly clean by design, the value of expecting literals can be even greater as the tests become more complex, thus harder to follow.

Expecting literals also makes your life easier when a test breaks. If neither your expected or actual values are literals, you will need to determine the value of both of them when a test breaks. Conversely, if your expected is a literal, only the actual value will need to be traced when a test is broken. For example the following tests both verify the result of the popularity method; however, the 2nd version is explicit in the value in expects.

require 'test/unit'

WEIGHT = 1.14

class Topic
def initialize(votes)
@votes = votes
end

def popularity
WEIGHT * @votes * @votes
end
end

class CircleTest < Test::Unit::TestCase
def test_popularity_with_variables
votes = 2
assert_equal WEIGHT * votes * votes, Topic.new(votes).popularity
end

def test_popularity_with_literals
assert_equal 4.56, Topic.new(2).popularity
end
end

In the above example, if the second test breaks it's because the calculation changed in some way. When the calculation changes, it's valuable to get feedback that it changed (by way of a breaking test). The first test doesn't use any literals, but it will not break if the value of WEIGHT is changed. Some people might consider this valuable or perhaps even more maintainable, I'd consider it a bug waiting to happen.

In reality, it's not likely that the programmer is responsible for setting the value of WEIGHT. Most businesses would have a subject matter expert that would give the value of weight and express (via requirements) the formula for popularity. The programmer would write the actual Ruby code, but using literal numbers for the expected value would allow the programmer to verify the functionality with the business. If in the future the business wants to change the value of WEIGHT, the programmers and the business could first change the tests to represent the new expected values, and then they could change the implementation. When you change code, it's always valuable to have a breaking test. Even if the business didn't sit with the programmer, the programmer could request the expected values as acceptance criteria. The tests will be easier to read, but they will also protect the business from a mistaken change to WEIGHT.

There are times when it's not possible to use a literal as the expected value. For example, when you need to assert a time or date you'll need to use a Time or Date object respectively. You can handle these situations in various ways. I prefer to use expected values that are as close to literals as I can get. This means using a literal as an argument to a method or calling to_s on the actual and expecting a string. The following two examples illustrate both of these techniques.

require 'test/unit'
require 'date'

class DatesTest < Test::Unit::TestCase
def test_valentines_day_last_year
assert_equal Date.parse("2/14/2007"), ValentinesDay.last_year
end

def test_valentines_day_last_year_to_s
assert_equal "2007-02-14", ValentinesDay.last_year.to_s
end
end

It's not always possible to use literals for expected values, but if you strive to use literals whenever possible your tests will be more readable and traceable in the future.

Labels: ,




Saturday, February 09, 2008

Designing a Domain Specific Language

I've worked on and with various Domain Specific Languages (DSLs), but I've found designing them to be the same process no matter what type of DSL is being created.

In March of 2005 I started one of the more technically interesting projects of my life. ThoughtWorks had been hired to design a system that would allow the director of finance for the client bank to edit, test, and deploy the business rules of the system without any assistance from IT. It was a bold step, but the return on investment was realized within weeks, not months (or worse, years).

One of the major challenges of designing this type of system is determining what the language should look like. Being new to the idea, we started out by telling the business what was possible. That was a mistake. The early versions of the language were easy for IT to work with, but were clunky for the business. The result was clearly suboptimal. As we continued evolving the language for new features we took a different approach, we asked the director what he wanted the language to look like. The more he shaped the language the easier it was for him to take ownership of the domain logic.

We figured out that we didn't need to write the domain logic in our language, we needed to make the domain language executable. The resulting project was a huge success, which led to me coming up with the term Business Natural Languages and presenting the idea at several conferences*.

Almost 3 years later, I decided I needed a new framework for unit testing. The primary motivation for designing a new framework was my desire for a more syntactically elegant way of defining tests. This time I knew I wanted to design what the language looked like and then worry about making it work at a later time. In fact, when writing expectations, I had a small suite of example tests before I had any way to execute them.

None of the languages I've helped design are flawless. But, I've seen significantly more success with the languages that were designed by a subject matter expert concerned with the language, instead of a language designer who was concerned with how to make the language execute.

* If you'd like to catch the Business Natural Language presentation you can see it at QCon London 2008 or rupy.eu 2008.

Labels:




ThoughtWorks DSL Podcast (part 2)

Part 2 of the DSL podcast Martin Fowler, Rebecca Parsons, Neal Ford and I participated in is now available. The podcast is once again being released as part of the ThoughtWorks IT Matters series and is available at ThoughtWorks: What we say.

Labels: , ,




Friday, February 08, 2008

Subversion: Rolling back revisions

Earlier this week someone mistakenly checked in some experimental code and wanted to back that code out. If you've never needed to do this, you might think that Subversion gives you a command to make this happen easily, it doesn't. In fact, I've found that to be a fairly common misconception.

There may be easier ways, but the following steps are the ones I take to roll back a specific revision.

Find the revision that you wish to back out. This can be done with various tools, but 'svn log --limit 10' will probably do the trick. Once you have the offending revision you are ready to get started. (Revision X in the example is the revision that needs to be rolled back and revision Y is the revision that came immediately before X [i.e. X-1])
  1. svn diff -rX:Y > a.patch
  2. patch -p0 < a.patch
  3. svn commit
note: while it's possible to give patch parameters and do this pretty much anywhere in the tree, I'd stick to doing this in the root folder of your project, for simplicity.

Again, there is probably an easier way to do this (which is part of my motivation for writing this entry), and obviously this solution depends on having 'patch' available.

Labels: ,




Monday, February 04, 2008

Static typing considered harmful

Given a good test suite the return on investment simply does not justify the use of static typing.
In 5 years, we'll view compilation as the weakest form of unit testing. -- Stuart Halloway
Type verification provides very little confidence that an application works. The little confidence it does provide comes at the cost of being confined by the type system. Verifying types is better than testing nothing at all. But, only verifying types is insufficient for application development.

The obvious answer is to write more tests. As you write more tests, you can verify independent pieces of the application and integration of the different pieces of the application. Currently, you know that your code compiles; but that is to general to be considered sufficient for verifying system behavior. Specific tests can verify specific behavior. You may start out testing that 1 + 1 is a number, but eventually you will be better off testing that 1 + 1 is 2. The test that 1 + 1 is a number may have given you some confidence that your application was working, but once you add a test to verify that 1 + 1 is 2, the test that verifies that 1 + 1 is a number no longer provides you with any additional confidence. Since the test that verifies 1 + 1 is a number no longer provides any value, it can safely be removed.

Labels: ,




Sunday, February 03, 2008

Behavior Based Testing

Behavior based testing is when you expect specific interactions to occur between objects when certain methods are executed. Behavior based testers (Mockists) utilize mocks to verify expected interaction between objects of a system. Behavior based testing is about specifying how the system should behave rather than specifying the expected result of running the system.

Behavior based testing has been criticized by state based advocates because it can cause test failures when implementation changes are made. In it's worst form, behavior based testing can be fragile. However, behavior based tests that avoid high implementation specification can effectively verify behavior without sacrificing maintainability.

The following test verifies the interaction between a Hotel instance and the ReservationService without the need to specify any other implementation detail. A change to book_for method will only cause the test to fail if the interaction between the two objects is changed, in which case a failing test is appropriate.

require 'test/unit'
require 'rubygems'
require 'mocha'

class ReservationService
# implementation
end

class HotelRoom
attr_accessor :booked
def book_for(customer)
reservation = ReservationService.reserve_for(customer, self)
self.booked = true
MaidService.notify(reservation) if reservation.tomorrow?
VipService.notify(reservation) if reservation.for_vip?
reservation.confirmation_number
end
end

class HotelRoomTests < Test::Unit::TestCase
def test_book_for_reserves_via_ReservationService
room = HotelRoom.new
ReservationService.expects(:reserve_for).with(:customer, room).returns(stub_everything)
room.book_for(:customer)
end
end

Behavior based tests are easy to spot because they focus on mock verification to ensure a system behaves as expected.

Developers who generally rely on behavior based tests can be considered Mockists. Martin Fowler has a great article, Mocks Aren't Stubs, that expands greatly on the ideas that are briefly mentioned here.

Labels: ,




State Based Testing

State based testing is when you exercise one or many methods of an object and then assert the expected state of the object. Many test-driven developers prefer state based tests because they specify as little implementation detail as possible. Tests that specify no implementation details should continue to pass even if the implementation of the methods being tested are changed. The following state based delegation test will continue to be valid for either of the following implementations.

require 'test/unit'

class ShowPresenterTests < Test::Unit::TestCase
def test_delegation_of_name_to_employee
employee = Employee.new("Shane Harvie")
assert_equal "Shane Harvie", ShowPresenter.new(employee).name
end
end

class Employee < Struct.new(:name); end

class ShowPresenter < Struct.new(:employee)
def name
employee.name
end
end

require 'forwardable'
class ShowPresenter < Struct.new(:employee)
extend Forwardable
def_delegator :employee, :name
end

State based tests can contain stubs, but they are generally avoided where possible. State based tests are easy to spot because they rely on assertions to verify the state of the application.

Developers who generally rely on state based tests can be considered Classicists. Martin Fowler has a great article, Mocks Aren't Stubs, that expands greatly on the ideas that are briefly mentioned here.

Labels: ,




Saturday, February 02, 2008

Ruby: Did TDD make Ruby a viable option?

Some people ask if Test Driven Development (TDD) is what caused Ruby to become popular. Other people smarter than me claim that TDD is in fact the reason that dynamic languages are now viable options.

I'm sorry, but I disagree.

Can you write a non-trivial application in Ruby without tests and have confidence in it? No. Can you write a non-trivial application in Java without tests and have confidence in it? Again, no.

A compiler lets you know that some things are correct, but should not give you confidence that your application behaves as expected. A trivial example is, I need to know that the calculate_tax (that's calculateTax for you Java fans) method returns 56, not that the result is a Fixnum (or Integer in Java).

As much as it should be, TDD is not mainstream. There's no TDD Conference, but RailsConf sells out every year.

I would actually credit David Heinemeier Hansson with making Ruby viable by creating a framework that drove mass adoption. But, I bet of you looked at the majority of Rails applications you would find empty test folders (or only the generated tests, which are never run). I'm quite sure that's true because I expect the conferences to attract the best of the Ruby developers, and several of the people I talk to at those conferences "simply don't have time" to write tests.

Labels: , ,




Testing: Mocha's stub_everything method

The Mocha mocking framework provides a method, stub_everything, that returns an object that will respond 'nil' to any message sent to it. The stub_everything method is indispensable for creating an object that is needed for testing, but the interactions are completely unimportant.

For example, in the following test the ReservationService::reserve_for method needs to return a duck that responds to the same methods that are defined on the (not shown) Reservation class. However, the focus of the test has nothing to do with the behavior of the reservation duck, so stub_everything is the perfect duck because it requires no additional set up and responds to all the methods required without error.

require 'test/unit'
require 'rubygems'
require 'mocha'

class ReservationService
# implementation
end
class MaidService
# implementation
end
class VipService
# implementation
end

class HotelRoom
def book_for(customer)
reservation = ReservationService.reserve_for(customer, self)
MaidService.notify(reservation) if reservation.tomorrow?
VipService.notify(reservation) if reservation.for_vip?
reservation.confirmation_number
end
end

class HotelRoomTests < Test::Unit::TestCase
def test_book_for_reserves_via_ReservationService
room = HotelRoom.new
ReservationService.expects(:reserve_for).with(:customer, room).returns(stub_everything)
room.book_for(:customer)
end
end

The stub_everything method is also nice because it can take parameters. For example you may want to test that the book_for method returns a confirmation number. The following test specifies the value of the confirmation_number value, but it uses stub_everything so that each other method call will return nil (without causing an error).

require 'test/unit'
require 'rubygems'
require 'mocha'

class ReservationService
# implementation
end
class MaidService
# implementation
end
class VipService
# implementation
end

class HotelRoom
def book_for(customer)
reservation = ReservationService.reserve_for(customer, self)
MaidService.notify(reservation) if reservation.tomorrow?
VipService.notify(reservation) if reservation.for_vip?
reservation.confirmation_number
end
end

class HotelRoomTests < Test::Unit::TestCase
def test_book_for_reserves_via_ReservationService
reservation = stub_everything(:confirmation_number => "confirmation number")
ReservationService.stubs(:reserve_for).returns(reservation)
assert_equal "confirmation number", HotelRoom.new.book_for(:customer)
end
end

Another usage of stub_everything that might not be immediately apparent is that it can be used as both a stub and a mock. The following test creates a stub and then sets an expectation on it. As a result, the expectation is verified (which is the focus of the test) and all other calls to the same object simply do nothing.

require 'test/unit'
require 'rubygems'
require 'mocha'


class HotelTests < Test::Unit::TestCase
def test_name_is_set_from_options
hotel = stub_everything
hotel.expects(:name=).with "Marriott"
Hotel.stubs(:new).returns hotel
Hotel.parse("Marriott||")
end
end

class Hotel
def self.parse(attributes)
hotel = self.new
hotel.name, hotel.location, hotel.capacity = attributes.split("|")
hotel
end
end

Using the stub_everything method of mocha can remove noise from tests and also make them more robust by ignoring all messages that are unimportant to the current test.

Labels: , , ,




Testing: High Implementation Specification Smell

At the end of my last blog entry, I touched briefly on the testing smell: High Implementation Specification. I got a fair amount of good feedback and based on several requests I decided it was worth creating a blog entry focused entirely on the smell.

Well written tests specify as little implementation detail as possible.

Developers new to mocking and stubbing tend to overuse the capabilities provided by mocking frameworks. I consider each call to a method of a mocking framework to be 1 implementation specification point. While using mocha I count a point for each of: stub, stub_everything, stubs, mock, expects, with, and returns. Any test that has an implementation specification score of 5 or more has a high implementation specification smell.

High Implementation Specification can stem from misusing mocking frameworks in various ways such as verifying parameters while stubbing methods, returning values that are never used, and specifying every interaction instead of using an object that will respond to any message.

Most mock frameworks provide the ability to verify the parameters sent to a mocked or stubbed method. When setting up a mock expectation, specifying the parameters can be useful for verifying interaction; however, when stubbing methods that are not the focus of the test, adding the additional parameter verification makes the test unnecessarily brittle.

The following code provides an example where the MainService notify method stub could verify the parameters, but the test is focused on testing the ReservationService. Given that the test is verifying the ReservationService there's no need for it to return a value and there's no need to verify the parameters sent to MaidService.notify. If the test did specify the parameters send to MaidService it would fail if those parameters changed. Since the test is verifying ReservationService, it would be disappointing if it failed due to a change in how MadService is called.

require 'test/unit'
require 'rubygems'
require 'mocha'

class ReservationService
# implementation
end
class MaidService
# implementation
end

class HotelRoom
attr_accessor :booked
def book_for(customer)
reservation = ReservationService.reserve_for(customer, self)
MaidService.notify(reservation)
end
end

class HotelRoomTests < Test::Unit::TestCase
def test_book_for_reserves_via_ReservationService
room = HotelRoom.new
ReservationService.expects(:reserve_for).with(:customer, room)
MaidService.stubs(:notify)
room.book_for(:customer)
end
end

Returning a value from a mocked method can be necessary, but should be avoided in general for two reasons: Returning a value signifies to someone reading a test that the value is necessary for some reason. Returning a value that is later verified with an assertion can cause false positives.

In the above example, the ReservationService does not provide a return value because that value isn't used anywhere within the test. If you read the above test and saw that a return value was specified you would probably want to spend time finding where and why it was used. Worse, if the above test returned a value and then later asserted equality the test would pass, but the implementation could change the return value in the future and the test would not break.

At times a mock or stub must be exercised in various ways within one test due to how the original method was written. However, specifying all the interactions within one test leads to a test that breaks with every implementation change. A better solution is to utilize an object that can respond to anything and only specify the behavior that the test focuses on.

The following example uses a stub_everything object to ensure that the location and capacity setting methods can be called without being specified, but it also expects that the name is set correctly. Using a stub_everything also ensures that if more attributes are set in the future the test will continue to pass without any intervention.

require 'test/unit'
require 'rubygems'
require 'mocha'


class HotelTests < Test::Unit::TestCase
def test_name_is_set_from_options
hotel = stub_everything
hotel.expects(:name=).with "Marriott"
Hotel.stubs(:new).returns hotel
Hotel.parse("Marriott||")
end
end

class Hotel
def self.parse(attributes)
hotel = self.new
hotel.name, hotel.location, hotel.capacity = attributes.split("|")
hotel
end
end

Labels: ,




This page is powered by Blogger. Isn't yours?