Using code generation in tests
2006-04-20
Not sure if this falls into the useful or into the weird category, but I’m beginning to do code generation in my behavior specifications…
Take a look at the behavior specification:
text/unit/transaction_state_test.rb
1 require File.dirname(__FILE__) + '/../test_helper' 2 3 class TransactionStateOrderingTest < Test::Unit::TestCase 4 TransactionState::StatusNames.each do |status| 5 define_method("test_#{status}_query_method") do 6 assert TransactionState.send(status).send("#{status}?"), status 7 (TransactionState::StatusNames - [status]).each do |inner_status| 8 assert !TransactionState.send(status).send("#{inner_status}?"), inner_status 9 end 10 end 11 end 12 end
And the corresponding implementation:
app/models/transaction_state.rb
1 class TransactionState 2 StatusNames = %w(started approved processed completed cancelled).freeze 3 4 attr_reader :name, :index 5 alias_method :to_s, :name 6 7 def initialize(name) 8 @name, @index = name.to_s, StatusNames.index(name.to_s) 9 raise "Unknown status name: #{name.inspect}" unless @index 10 end 11 12 # Generate instance methods to query the state of this instance. 13 # Generates #started? and #completed?, among others. 14 StatusNames.each do |status| 15 define_method("#{status}?") do 16 self.name == TransactionState.send(status).name 17 end 18 end 19 20 # Generate class methods to return pre-instantiated 21 # TransactionState objects, one per status name. 22 class << self 23 @@states_cache = Hash.new 24 StatusNames.each do |status| 25 @@states_cache[status.to_sym] = TransactionState.new(status) 26 define_method(status) do 27 @@states_cache[status.to_sym] 28 end 29 end 30 end 31 32 Statuses = StatusNames.map {|status| TransactionState.send(status)} 33 end
Nothing too earth shattering, except for the specs. How do I ensure the specs were properly generated ? I didn’t, except for confirming that the number of tests and assertions went up as expected.
blog comments powered by Disqus