Wednesday, December 27, 2006

Rails: Unit Testing ActiveRecord Validations

There are at least 2 easy ways to unit test the validations of an ActiveRecord::Base subclass.

The most straight-forward way is to create a model and use the valid? method to determine if it is valid or not. For an example I'm going to reuse the PhoneNumber class I defined in a previous entry on ActiveRecord Unit Testing. The following code shows the three tests I've written to ensure that I've correctly defined a validates_presence_of for digits.
class PhoneNumberTest < Test::Unit::TestCase
Column = ActiveRecord::ConnectionAdapters::Column

test "digits are required to be valid" do
PhoneNumber.stubs(:columns).returns([Column.new("digits", nil, "string", false)])
number = PhoneNumber.new(:digits => "1234567890")
assert_equal true, number.valid?
end

test "invalid with no digits " do
PhoneNumber.stubs(:columns).returns([Column.new("digits", nil, "string", false)])
number = PhoneNumber.new()
assert_equal false, number.valid?
end

test "errors include digits on invalid digits" do
PhoneNumber.stubs(:columns).returns([Column.new("digits", nil, "string", false)])
number = PhoneNumber.new()
number.valid?
assert_equal "can't be blank", number.errors.on('digits')
end

end
After adding the validates_presence_of call to the PhoneNumber class, all the above tests pass.
class PhoneNumber < ActiveRecord::Base
validates_presence_of :digits
end
This all works fine, but I'm not sure it's necessary to actually cause a validation to occur just to test that one has been defined.

An alternative test implementation could mock the validates_presence_of call and load the file. If the validates_presence_of is defined correctly the class methods will be invoked.
class PhoneNumberTest < Test::Unit::TestCase
Column = ActiveRecord::ConnectionAdapters::Column

test "validates_presence_of is defined for digits" do
PhoneNumber.expects(:validates_presence_of).with(:digits)
load "#{RAILS_ROOT}/app/models/phone_number.rb"
end

end
This method of testing doesn't require actually creating a PhoneNumber instance, instead it tests that the PhoneNumber validations are defined correctly.

The load call is a bit messy; however, we can clean that up by adding another method to ActiveRecord::Base.
class << ActiveRecord::Base
def standard_path
File.expand_path("#{RAILS_ROOT}/app/models/#{self.name.underscore}.rb")
end
end
Following the above code change the test can be rewritten to the code shown below.
class PhoneNumberTest < Test::Unit::TestCase
Column = ActiveRecord::ConnectionAdapters::Column

test "validates_presence_of is defined for digits" do
PhoneNumber.expects(:validates_presence_of).with(:digits)
load PhoneNumber.standard_path
end

end
Which implementation is better? Both implementations provide pros and cons. I suggest trying both out to determine which works best for you (or your team).

Monday, December 25, 2006

Rails: ActiveRecord Unit Testing

There's been a fair amount of information posted recently on the topic of unit testing ActiveRecord::Base subclasses. Of all the information, I think the most valuable was James' observation that a better data model would result in better tests with less dependencies.

That said, sometimes it would be nice to truly unit test an ActiveRecord::Base subclass. When I say unit test, I mean no dependency on the database. For example, I may store a phone number in the database as a 10 digit string, but I may want to expose that phone number with formatting. I may also want to expose the area code, exchange, and station as methods of the PhoneNumber class. To test this behavior I wrote the following tests that do hit the database.
class PhoneNumberTest < Test::Unit::TestCase

test "to_formatted_s returns US format" do
number = PhoneNumber.new(:digits => "1234567890")
assert_equal "(123) 456-7890", number.to_formatted_s
end

test "area code returns first 3 numbers" do
number = PhoneNumber.new(:digits => "1234567890")
assert_equal "123", number.area_code
end

end
If this syntax looks bizarre at all, you might want to read about how these tests take advantage of the test class method.

The PhoneNumber class has the following implementation.
class PhoneNumber < ActiveRecord::Base

def to_formatted_s
'(' + area_code + ') ' + exchange + '-' + station
end

def area_code
digits[0..2]
end

def exchange
digits[3..5]
end

def station
digits[6..9]
end

end
As the implementation shows, the digits are stored in the database. Splitting the digits up or formatting them is handled by methods on the model.

Now that we have a few tests we'll add the code that disallows database access from unit tests and rerun the tests. As expected the tests fail with the following error.
1) Error:
test_area_code_returns_first_3_numbers(PhoneNumberTest):
ArgumentError: You cannot access the database from a unit test
Looking at the stack trace provided by the error you can track the database access to the columns method of ActiveRecord::Base.

If you want to unit test a model the columns method is a good one to stub. I tried a few different options and the best one I found was stubbing the columns method with a method that returns an array of ActiveRecord::ConnectionAdapters::Column instances. Since I only needed the digits attribute for these tests, it was the only column I needed to return in the array. The following tests test my PhoneNumber class without requiring a trip to the database.
class PhoneNumberTest < Test::Unit::TestCase
Column = ActiveRecord::ConnectionAdapters::Column

test "to_formatted_s returns US format" do
PhoneNumber.stubs(:columns).returns([Column.new("digits", nil, "string", false)])
number = PhoneNumber.new(:digits => "1234567890")
assert_equal "(123) 456-7890", number.to_formatted_s
end

test "area code returns first 3 numbers" do
PhoneNumber.stubs(:columns).returns([Column.new("digits", nil, "string", false)])
number = PhoneNumber.new(:digits => "1234567890")
assert_equal "123", number.area_code
end

end

Rails: Migrations with a large team part I

I've been working on a big (in Rails terms) team for a few months now and we've come to some conclusions concerning Migrations. I'll start with the bad and in part II I'll talk about the decisions we made and why.

Migrations are great, but they do come at a cost. When working with a large team (my current team size is 14 and growing) migration conflicts happen. This can be mitigated with communication, but migrations can definitely be a bottleneck. Also, the process of creating a migration can be painful on a large team. Before creating a migration you should always update from source control to ensure you get all the migrations checked in by your teammates. Then, the best case scenario is when you can create a migration that doesn't change anything and immediately check it in. Checking in a new migration immediately helps ensure you don't block other teammates from creating migrations; however, it's not always as simple as adding a new table. Migrations that alter the database structure often break several tests. Obviously, you can't check those migrations in until you fix all the breaking tests, which can take time. During this time, database changes are blocked for the entire team.

It can also be troublesome to find specific changes to the database within multiple migration files. Finding which migration adds a specific column can take a fair amount of time when you are working with over 50 migration files. Naming conventions can mitigate this issue some; however, naming conventions generally require that only one action occur per file. For example, the 023_create_customers_table.rb file can only create the customer table and cannot alter the purchases table to add the customer_id column. This type of naming convention helps on searching for specific changes to the database; however, it also results in a large number of migration files.

Sunday, December 24, 2006

Ruby: Alias method alternative

Originally blogged by Martin @ split-s. I'm reposting because I don't see this method used very often.

Using Ruby's alias it is possible to reopen a class, override a method and still call the original.
class ClockRadio
def on!
@on = true
end

def on?
@on
end
end

class ClockRadio
alias :old_on! :on!

def on!
old_on!
@display_time = true
end

def display_time?
@display_time
end
end
While this works, it can cause unexpected results and leave around artifacts. In isolation this doesn't look risky; however, in a large codebase someone could easily define old_on! or use it as their alias name also. The other, much smaller, issue is that old_on! will be left as a method on ClockRadio when you actually have no desire to expose this method outside of calling it from on!.

An alternative is to capture the on! method as an unbound method, bind it to the current instance, and call it explicitly.
class ClockRadio
on = self.instance_method(:on!)

define_method(:on!) do
on.bind(self).call
@display_time = true
end

def display_time?
@display_time
end
end
The above version ensures that the correct version of on! will be called from the on! implementation defined in the reopened version ClockRadio. This version also lets the reference to the old_on! fall out of scope after the class is defined; therefore, there are no additional methods left around as side effects.

Below are the tests that prove the concept.
class AliasMethodAlternativeTest < Test::Unit::TestCase

def test_aliased_method_returns_true_for_on
radio = ClockRadio.new
radio.on!
assert_equal true, radio.on?
end

def test_aliased_method_returns_true_for_display_time
radio = ClockRadio.new
radio.on!
assert_equal true, radio.display_time?
end

end

Saturday, December 23, 2006

Ruby: Multiline strings - here doc or quotes

Ruby supports multiline strings by providing two types of here doc syntax. The first syntax uses and additional dash, but allows you to indent the "end of here doc" delimiter ('eos' in the example).
  <<-eos
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.
eos
Another here doc syntax doesn't require you to use the dash, but it does require that the "end of here doc" delimiter is in column 1 (or there are no spaces that precede it).
  <<eos
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.
eos
Both options support the following syntax when passed as a parameter.
  Content.new(:value => <<eos)
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
eos

Content.new(:value => <<eos
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
eos
)
As an alternative, you could also pass the parameter using quotes.
  Content.new(:value => "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor 
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia")
The solution with quotes requires less lines, so I tend to prefer it. Also, the only benefit I can think of for the here doc syntax is that it allows quotes without having to escape them.

Friday, December 22, 2006

Ruby: Constant values

I generally use constants in Ruby for the following two situations: Markers or Constant values.

Markers are used as a standard for comparison.
module CreditCardTypes
Visa = 0
Mastercard = 1
end
Markers are initialized to a value; however, that value is unimportant as long as it is unique. Markers are generally used in an application within conditional statements.
case card.type
when CreditCardTypes::Visa then VisaLuhnValidator
when CreditCardTypes::Mastercard then MastercardLuhnValidator
end
Note: you can also use symbols as markers, but I prefer constants. This preference is based on the fact that if I mistype CreditCardTypes::Vissa it will fail fast; however, if I mistype :credit_card_type_vissa, I will get a possibly hard to find bug.

Constant values are global values that should never change during the life of your application.
module MathValues
PI = 3.14
end
Constant values can be used throughout applications to ensure that the same value is consistently used.
circumference = circle.diameter * MathValues::PI
Based on these usages, I'm a bit concerned about some behavior I recently found.
irb(main):019:0> module MathVariables
irb(main):020:1> PI = 3.14
irb(main):021:1> end
=> 3.14
irb(main):022:0> module MathVariables
irb(main):023:1> PI = 3.14159265
irb(main):024:1> end
(irb):23: warning: already initialized constant PI
=> 3.14159265
Warning? This means anyone can redefine my constants at any time? Did I do something wrong? Does anyone else think this is dangerous?

Thursday, December 21, 2006

Rake: db:migration:conflict

This task originally written by Stephen Chu.

If you are using migrations on a large team, migration conflicts can and do occur. To help avoid conflicts you can add the following task to your codebase and run it after updating and before checking in (hopefully in your commit task).
namespace :db do
namespace :migration do
desc "After downloading migrations from server, check to see if we have conflicting migration numbers."
task :conflict do
existing_migration_numbers = []
Dir.entries("#{RAILS_ROOT}/db/migrate").each do |entry|
migration_number = entry.scan(/^\d+/).first
next if migration_number.nil?
if existing_migration_numbers.include? migration_number
raise ArgumentError, "Migration #{migration_number} is already checked in on the server. Verify your migration numbers."
end
existing_migration_numbers << migration_number
end
end
end
end

Ruby: rake commit

update: Paul Gross created a GitHub project where these tasks live and can be improved. Check it out at http://github.com/pgr0ss/rake_commit_tasks/tree/master

I wrote in the past about rake pc, the task that I run before checking in. The rake pc task is good, but it stops one step short: actually checking in.

The first implementation of this task used a task with parameters; however, the final implementation uses STDIN.gets and a data file.

The rake commit task also allows you specify a pair if you are working on multiple pairing stations.

desc "Run to check in"
task :commit => :pc do
commit_pair = retrieve_saved_data "pair"
commit_message = retrieve_saved_data "message"
command = %[svn ci -m "#{commit_pair.chomp} - #{commit_message.chomp}"]
puts command
puts %x[#{command}]
end

def retrieve_saved_data attribute
data_path = File.expand_path(File.dirname(__FILE__) + "/#{attribute}.data")
`touch #{data_path}` unless File.exist? data_path
saved_data = File.read(data_path)

puts "last #{attribute}: " + saved_data unless saved_data.chomp.empty?
print "#{attribute}: "

input = STDIN.gets
while (saved_data.chomp.empty? && (input.chomp.empty?))
print "#{attribute}: "
input = STDIN.gets
end
if input.chomp.any?
File.open(data_path, "w") { |file| file << input }
else
puts "using: " + saved_data.chomp
end
input.chomp.any? ? input : saved_data
end

Sunday, December 17, 2006

BNL: New location

Instead of continuing to post BNL updates to my blog I've put all the current content on http://bnl.jayfields.com.

I'll post an occasional blog entry here when significant updates occur.

Rails: Plugins, Why?

I've been wondering for awhile if the Plugin system is something worth taking out of Rails. It's not that I have anything against Rails plugins, but I cant find any way in which they are superior to RubyGems.

Two benefits I have heard are: The code can be packaged with the application and Rails auto-loads plugins. While I agree that these two things are important, it's possible to achieve the first by using gem unpack and it's also possible to auto-load unpacked gems.

Update: I hadn't even seen this post about Rails autoloading gems, by Chad, until today. It seems that more than a few people would like their gems to play nicely with Rails.

Another benefit I've heard is that plugins are easier to create. I've never created a plugin, but Dr Nic has made it quite easy to create gems using newgem.

While I cant find any features that plugins provide and gems do not, RubyGems do provide features that are not available in plugins such as versioning and dependencies.

So, a question and a request:

What benefits do plugins provide?

If there are no benefits to plugins, create gems for your reusable Ruby code instead of plugins.

Ruby: instance and class methods from a module

Context: You have a presenter class that needs to validate attributes. You like the way ActiveRecord allows you to define validations using class methods. You also want to use a valid? method on your presenter instances to determine if they are valid or not.

Step one: You define the validation methods in a module.
module Presenters::Validations
def validates_presence_of(*args)
..
end

def validates_format_of(*args)
..
end
end
Then, you extend the module to add the class methods to your presenter.
class AccountInformationPresenter
extend Presenters::Validations

validates_presence_of :username, :password
..
This implementation also requires that you define valid? in each presenter class.
  def valid?
self.class.validations.collect do |validation|
unless validation.valid?(self)
self.errors.add(validation.on, validation.message)
end
end.all?
end
end
When building the second presenter it should be clear that the valid? should be abstracted. This abstraction could result in another module. The new module could be included thus providing valid? as an instance method.

Step two: Another common approach is to define the class methods in a ClassMethods module inside the Presenters::Validations module.
module Presenters::Validations
module ClassMethods
def validates_presence_of(*args)
..
end

def validates_format_of(*args)
..
end
end

def self.included(base)
base.extend(ClassMethods)
end

def valid?
self.class.validations.collect do |validation|
unless validation.valid?(self)
self.errors.add(validation.on, validation.message)
end
end.all?
end
end
This approach, while common in Rails, can generate some dislike. A counter argument is that include is designed to add instance methods and using self.included is clever, but provides unexpected behavior. I've found that people who dislike the self.included trick prefer to explicitly use both include and extend.
class AccountInformationPresenter
include Presenter::Validations
extend Presenter::Validations::ClassMethods

..
end
Which approach is better?
I tend to prefer explicitness; however, if something is used often enough it can turn from an anti-pattern to an idiom. I'm not sure if that is the case or not here, but I think it might be.

Saturday, December 16, 2006

Ruby: Use class methods to reduce duplication

Context: I had two method definitions in my class that were similar to the code below.
class TaxRemoteFacade

def state_tax
...
@remote.close
end

def federal_tax
...
@remote.close
end

end
I've only shown two, but we actually needed the @remote.close code at the end of several methods.

Solution: To reduce the duplication you could introduce a mixin with a method to remove the duplication.
module RemoteFacade
def remote_call(method_name, &block)
class_eval do
define_method name do
instance_eval &block
@remote.close
end
end
end
end
The TaxRemoteFacade can now be defined as the code below.
class TaxRemoteFacade
extend RemoteFacade

remote_call do :state_tax do
...
end

remote_call do :federal_tax do
...
end

end

Thursday, December 14, 2006

Rails: Running migrations in another environment

From the command line it's possible to run migrations in any environment. For example, using the code below it is possible to run migrations in the test environment.
rake db:migrate RAILS_ENV=test
Because the above code works it would appear that you could create the following rake task to automate running the migrations in the test environment.
task :test_migrate do
ENV["RAILS_ENV"] = "test"
Rake::Task["db:migrate"].invoke
end
Unfortunately, that didn't work, but the following change does work.
task :test_migrate do
ActiveRecord::Base.establish_connection "test"
Rake::Task["db:migrate"].invoke
end

Wednesday, December 13, 2006

Rake: --dry-run, --trace, and -T

When using rake it can often be useful to know the order in which tasks are being executed. To list the execution order you can issue the following command.
rake --dry-run
The --dry-run option works well; however, it does not report tasks that are explicitly invoked.
Rake::Task["test:units"].invoke
The above code can be used to explicitly invoke a task (test:units in the example). Rails uses this in various database and testing tasks. However, it's still possible to see which tasks are being executed. The following code will show each task as it's executed.
rake --trace
The --trace option provides a lot of information, but if you have grep available it can be trimmed down easily.
rake --trace | grep Execute
Also, when running rake with the -T option you can also pass another parameter to list only the commands that contain that parameter. For example, the following code returns all the tasks that contain 'db' in their description.
rake -T db

Tuesday, December 05, 2006

BNL: Extracting Sales Person

Versioning on an individual sales person
Before we add version control to our application we need something to version. Logically it seems you would like to have a document history showing compensation agreements for each sales person. To accommodate this requirement, we can pull the sales person from the scripts and save them off separately. The first step is creating migrations that create and alter the tables to match our new intentions.

File: 003_create_sales_people.rb
class CreateSalesPeople < ActiveRecord::Migration
def self.up
create_table :sales_people do |t|
t.column :name, :string
end
end

def self.down
drop_table :sales_people
end
end

File: 004_add_sales_person_to_compensation_script.rb
class AddSalesPersonToCompensationScript < ActiveRecord::Migration
def self.up
add_column :compensation_scripts, :sales_person_id, :integer
add_column :compensation_scripts, :created_at, :datetime
end

def self.down
remove_column :compensation_scripts, :sales_person_id
remove_column :compensation_scripts, :created_at
end
end


Following the above migrations the existing codebase will need to be updated to reflect the new database structure. The "new" page has been updated to include a textbox for entering the sales person's name.

File: new.rhtml
<% form_for :script, @script, :url => { :action => "create" } do |form| %>
Name<br>
<%= form.text_field :name %><br>
<br>
Compensation Rules<br>
<%= form.text_area :logic %><br>
<%= submit_tag %>
<% end %>


Introducing the name textbox requires us to create the new SalesPersonCompensationScript class. This is because a CompensationScript still contains the logic, but the SalesPerson class maintains the name captured in the "name" textbox. The SalesPersonCompensationScript is a simple class that is used only to store the data that will be used by the controller to save a SalesPerson and CompensationScript.

File: sales_person_compensation_script.rb
class SalesPersonCompensationScript
attr_accessor :name, :logic

def initialize(hash=nil)
unless hash.nil?
self.name = hash[:name]
self.logic = hash[:logic]
end
end
end


The PayrollController has been updated to use the SalesPersonCompensationScript class on the "new" view and on the create action. The create action is where the SalesPersonCompensationScript is used to create a CompensationScript and a new SalesPerson if necessary.

File: payroll_controller.rb
class PayrollController < ApplicationController

def index
@sales_people = SalesPerson.find :all
end

def new
@script = SalesPersonCompensationScript.new
end

def create
person_script = SalesPersonCompensationScript.new(params[:script])
script = CompensationScript.create(:logic => person_script.logic)
sales_person = SalesPerson.find_by_name(person_script.name) ||
SalesPerson.create(:name => person_script.name)
sales_person.compensation_scripts << script
redirect_to :action => :index
end

def view
@person = SalesPerson.find(params[:person_id])
end

def execute
@person = SalesPerson.find(params[:person_id])
vocabulary = InlineVocabulary.new(@person.short_name)
logic = @person.active_compensation_script.logic
@compensation = CompensationParser.parse(logic, vocabulary, InlineContext.new)
end

def execute_all
@compensations = {}
SalesPerson.find(:all).each do |person|
vocabulary = SqlVocabulary.new(person.short_name)
logic = person.active_compensation_script.logic
compensation = CompensationParser.parse(logic, vocabulary, SqlContext.new)
@compensations[person.name.to_sym] = compensation.amount
end
end

end


Every action within the PayrollController has been changed to reflect the new domain model that includes a SalesPerson as a concept. The index action now relies on an array of SalesPerson instances instead of an array of CompensationScript instances. The index.rhtml has also been updated to reflect this change.

File: index.rhtml
Employees:
<ul>
<% @sales_people.each do |person| %>
<li>
<%= person.name %> -
<%= link_to 'view', :action => :view, :person_id => person.id %> |
<%= link_to 'execute', :action => :execute, :person_id => person.id %>
</li>
<% end %>
</ul>
<%= link_to 'Execute All', :action => :execute_all %> |
<%= link_to 'Create new compensation script', :action => :new %>


Navigating to the view page now requires a sales person instance to be available.

File: view.rhtml
Compensation rules for <%= @person.name %>
<pre><%= @person.active_compensation_script.logic %></pre>
<%= link_to 'back', :action => :index %>


The execute.rhtml was updated also, but only changed slightly to accommodate the introduction of the sales person concept.

File: execute.rhtml
<%= @person.name %> compensation: <%= number_to_currency @compensation.amount %><br>
<%= link_to 'back', :action => :index %>


The execute action in PayrollController relies on the short name of a sales person, and the view.rhtml page uses the active_compensation_script method of the SalesPerson instance.

File: sales_person.rb
class SalesPerson < ActiveRecord::Base
has_many :compensation_scripts

def active_compensation_script
compensation_scripts.sort.first
end

def short_name
self.name.gsub(/^([A-Z])[a-z]+\s/, '\1').downcase
end
end


The execute_all.rhtml also contains minor updates that reflect the introduction of SalesPerson

File: execute_all.rhtml
<% @compensations.each_pair do |name, amount| %>
<%= name %> compensation: <%= number_to_currency amount %><br>
<% end %>
<%= link_to 'back', :action => :index %>


All these changes also mean the CompensationScript and Root classes have been simplified by removing the name of the employee.

File: compensation_script.rb
class CompensationScript < ActiveRecord::Base
def <=>(other)
other.created_at <=> self.created_at
end
end


File: root.rb
class Root
extend Vocabulary

def initialize(vocabulary, eval_context)
@compensations = []
@vocabulary, @eval_context = vocabulary, eval_context
end

def amount
@compensations.collect do |compensation|
compensation.amount
end.inject { |x, y| x + y }
end

def process(line)
instance_eval(line)
end

phrase :compensate do
@compensations << Compensation.new(@vocabulary, @eval_context)
@compensations.last
end

end


Lastly, the Employee class can be completely removed since Root no longer parses an employee's name.

Saturday, December 02, 2006

xUnit: Absence of Mocks and Stubs

In September of 2004 I met James Newkirk at the Microsoft Patterns and Practice Summit in Reston Virginia. If you don't know James, among other accomplishments he was one of the creators of NUnit. At the summit I stopped him to express my appreciation for NUnit. A conversation ensued in which he caught me off guard with the question: What is missing from NUnit?

Well, it's been over two years, but I finally have an answer: Mocks and Stubs

As long as I've needed them, Mocks have been available as additional libraries. When I was using NUnit I used NMock and when using Test::Unit I use Mocha. But, why are these libraries not simply included in xUnit frameworks?

In 2004 I was also introduced to Behavioral Testing as a formal concept. I had been using mocks for a bit; however, it was wile reading Mocks Aren't Stubs by Martin Fowler when the difference between state and behavioral testing became clear. Martin concludes the write up with the following question and answer:
So which style is the best?

I find this a difficult question to answer with confidence.
It's been more than two years since Mocks Aren't Stubs was originally published and both behavioral and state based testing still exist. I'm skeptical that either will ever be declared better for all cases.

I can't imagine having to choose either behavioral or state based testing exclusively. Both have their pros and cons and, I believe, should be used accordingly. So why then do xUnit implementations continue to ignore support for behavioral testing?

Given that the tests we write as developers have matured, I believe it's fair to expect our xUnit frameworks to evolve with our needs.