Class: Arrow::AppletTestCase

Inherits:
Test::Unit::TestCase
  • Object
show all
Includes:
Loggable, FlexMock::TestCase, Test::Unit::Assertions
Defined in:
lib/arrow/applettestcase.rb

Overview

This is an abstract test case class for building Test::Unit unit tests for Arrow applets.

Synopsis

$LOAD_PATH.unshift “tests/lib” unless $LOAD_PATH.include?(“tests/lib”) require ‘applettestcase’

class MySomethingTest < Arrow::AppletTestCase

applet_under_test “jobs”

def test_default_request_should_return_object_list end

def test_default_request_with_oid_arg_should_display_details set_request_params :oid => 13 end

end

VCS Id

$Id$

Authors

Copyright © 2003, 2004, 2006 RubyCrafters, LLC.

This work is licensed under the Creative Commons Attribution License. To view a copy of this license, visit creativecommons.org/licenses/by/1.0 or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.

Constant Summary collapse

APPLET_PATH =

The default path to the directory where applets live

Pathname.new( $0 ).expand_path.dirname + "applets"
AnsiAttributes =

Set some ANSI escape code constants (Shamelessly stolen from Perl’s Term::ANSIColor by Russ Allbery <[email protected]> and Zenin <[email protected]>

{
	'clear'      => 0,
	'reset'      => 0,
	'bold'       => 1,
	'dark'       => 2,
	'underline'  => 4,
	'underscore' => 4,
	'blink'      => 5,
	'reverse'    => 7,
	'concealed'  => 8,

	'black'      => 30,   'on_black'   => 40, 
	'red'        => 31,   'on_red'     => 41, 
	'green'      => 32,   'on_green'   => 42, 
	'yellow'     => 33,   'on_yellow'  => 43, 
	'blue'       => 34,   'on_blue'    => 44, 
	'magenta'    => 35,   'on_magenta' => 45, 
	'cyan'       => 36,   'on_cyan'    => 46, 
	'white'      => 37,   'on_white'   => 47
}

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ AppletTestCase

Check to be sure an applet has been associated before instantiation.



239
240
241
242
# File 'lib/arrow/applettestcase.rb', line 239

def initialize( *args ) # :notnew:
    throw :invalid_test unless self.class.appletclass
    super
end

Class Attribute Details

.appletclassObject

Returns the value of attribute appletclass.



96
97
98
# File 'lib/arrow/applettestcase.rb', line 96

def appletclass
  @appletclass
end

.appletnameObject

Returns the value of attribute appletname.



96
97
98
# File 'lib/arrow/applettestcase.rb', line 96

def appletname
  @appletname
end

.fixture_dataObject

Returns the value of attribute fixture_data.



96
97
98
# File 'lib/arrow/applettestcase.rb', line 96

def fixture_data
  @fixture_data
end

Class Method Details

.ansicode(*attributes) ⇒ Object

Returns a String containing the specified ANSI escapes suitable for inclusion in another string. The attributes should be one or more of the keys of AnsiAttributes.



127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/arrow/applettestcase.rb', line 127

def self::ansicode( *attributes )
	return '' unless /(?:xterm(?:-color)?|eterm|linux)/i =~ ENV['TERM']

	attr = attributes.collect {|a|
		AnsiAttributes[a] ? AnsiAttributes[a] : nil
	}.compact.join(';')
	if attr.empty? 
		return ''
	else
		return "\e[%sm" % attr
	end
end

.applet_under_test(applet) ⇒ Object

Define the name of the applet under test. The given name will be stringified, downcased, and searched for in the #applet_path.



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/arrow/applettestcase.rb', line 201

def self::applet_under_test( applet )
	if applet.is_a?( Class )
		self.appletclass = applet
		self.appletname = applet.signature.name
	else
		debug_msg "Setting applet under test for testcase: %p" % [self]

		if Arrow::Applet.derivatives.empty?
            Pathname.glob( APPLET_PATH + '**/*.rb' ).each do |appletfile|
    		    debug_msg "Trying to load #{appletfile}"
				begin
					Arrow::Applet.load( appletfile )
				rescue LoadError
				end
    	    end
        end

		# :view_template becomes /view[-_]template/
		applet_pat = Regexp.new( applet.to_s.gsub(/_/, '[-_]?') )
	
		self.appletclass = Arrow::Applet.derivatives.find {|klass|
			debug_msg "  Checking applet '#{klass.name.downcase}' =~ #{applet_pat}..."
			applet_pat.match( klass.name.downcase ) or
				applet_pat.match( klass.filename )
		} or raise "Failed to load applet matching #{applet_pat}"
		self.appletname = applet.to_s

		debug_msg "Applet under test is: #{self.appletclass}"
	end
end

.debug_msg(*msgs) ⇒ Object

Output the specified msgs joined together to STDERR if $DEBUG is set.



143
144
145
146
147
# File 'lib/arrow/applettestcase.rb', line 143

def self::debug_msg( *msgs )
	return unless $DEBUG
	self.message "%sDEBUG>>> %s %s" %
		[ ansicode('dark', 'white'), msgs.join(''), ansicode('reset') ]
end

.message(*msgs) ⇒ Object

Output the specified msgs joined together to STDOUT.



152
153
154
155
# File 'lib/arrow/applettestcase.rb', line 152

def self::message( *msgs )
	$stderr.puts msgs.join('')
	$stderr.flush
end

.set_fixture_data(model_class, data) ⇒ Object

Set up model data for the given model_class that can be used in the stub data classes. The data argument is an Array of data, and can be either Hashes or Arrays. If Arrays are used, the first one in the list should be a list of fields, and each subsequent Array should be field values. E.g.,

[
  [ :title, :description, :date ],
  [ "title1", "desc1",    Date.today ],
  [ "title2", "desc2",    Date.today-4 ],
]

which is equivalent to:

[
  { :title => "title1", :description => "desc1", :date => Date.today }
  { :title => "title1", :description => "desc1", :date => Date.today }
]


173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/arrow/applettestcase.rb', line 173

def self::set_fixture_data( model_class, data )
    @fixture_data ||= {}
    
		# [ [<fields>], [<values1], [<values2>] ]
    if data.first.is_a?( Array )
			fields = data.shift
			objects = data.collect do |row|
obj = OpenStruct.new
fields.each_with_index {|f,i| obj.__send__(f, row[i])}
			end

		# [ {:field1 => <value1>}, {:field1 => <value2>} ]
		elsif data.first.is_a?( Hash )
			objects = data.collect do |row|
OpenStruct.new( row )
			end

		# Custom objects (Mocks, etc.)
		elsif data.is_a?( Array )
			objects = data
		end	
    
    @fixture_data[ model_class ] = objects
end

Instance Method Details

#ansicode(*attributes) ⇒ Object

Instance-alias for the like-named class method



298
299
300
# File 'lib/arrow/applettestcase.rb', line 298

def ansicode( *attributes )
	self.class.ansicode( *attributes )
end

#create_mock(name) ⇒ Object

Create a new mock object and register it to be verified at the end of the test.



469
470
471
# File 'lib/arrow/applettestcase.rb', line 469

def create_mock( name )
       return flexmock( name )
end

#debug_msg(*msgs) ⇒ Object

Instance alias for the like-named class method



304
305
306
# File 'lib/arrow/applettestcase.rb', line 304

def debug_msg( *msgs )
	self.class.debug_msg( *msgs )
end

#default_delegation_behaviorObject

The default block passed to Arrow::Applet#delegate by #with_fixtured_delegation if no block is passed to either #should_delegate or #should_not_delegate. If you override this, you should either super to this method or set @delegate_called yourself.



412
413
414
# File 'lib/arrow/applettestcase.rb', line 412

def default_delegation_behavior
	@delegate_called = true
end

#extract_parameters(queryhash, key = nil) ⇒ Object

Extract parameters for the given key from the given queryhash using the form validator for the current action and return it.



488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/arrow/applettestcase.rb', line 488

def extract_parameters( queryhash, key=nil )
	profile = @applet.signature.validator_profiles[ @action ] ||
		@applet.signature_profiles[ :__default__ ]
	validator = Arrow::FormValidator.new( profile )
	
	validator.validate( queryhash )

	if key
		return validator.valid[ key ]
	else
		return validator.valid
	end
end

#fixture_session(txn) ⇒ Object

Set up a mock object as the given transaction’s session.



475
476
477
478
479
480
481
482
483
# File 'lib/arrow/applettestcase.rb', line 475

def fixture_session( txn )
	session = create_mock( "session" )
	txn.should_receive( :session ).
	    and_return( session ).zero_or_more_times
	session.should_receive( :[] ).and_return( nil ).zero_or_more_times
	session.should_receive( :[]= ).and_return( nil ).zero_or_more_times
	
	return session
end

#hrule(length = 75, char = "-") ⇒ Object

Return a separator line made up of length of the specified char.



311
312
313
# File 'lib/arrow/applettestcase.rb', line 311

def hrule( length=75, char="-" )
	return (char * length ) + "\n"
end

#hrule_section(content, caption = '') ⇒ Object

Return a section delimited by hrules with the specified caption and content.



317
318
319
320
321
322
323
# File 'lib/arrow/applettestcase.rb', line 317

def hrule_section( content, caption='' )
	caption << ' ' unless caption.empty?
	return caption +
		hrule( 75 - caption.length ) +
		content.chomp + "\n" +
		hrule()
end

#message(*msgs) ⇒ Object

Instance alias for the like-named class method.



292
293
294
# File 'lib/arrow/applettestcase.rb', line 292

def message( *msgs )
	self.class.message( *msgs )
end

Output a header for delimiting tests



327
328
329
330
331
# File 'lib/arrow/applettestcase.rb', line 327

def print_test_header( desc )
	return unless $VERBOSE || $DEBUG
	message "%s>>> %s <<<%s" % 
		[ ansicode('bold','yellow','on_blue'), desc, ansicode('reset') ]
end

#run(result) ⇒ Object

Output the name of the test as it’s running if in verbose mode, and support per-test debugging settings.



280
281
282
283
# File 'lib/arrow/applettestcase.rb', line 280

def run( result )
	print_test_header self.name
	super
end

#setupObject

Set up a test with some useful test objects



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/arrow/applettestcase.rb', line 246

def setup
	super

	debug_msg "%p: Setting up test for applet %p" % 
		[self.class, self.class.appletclass]

	# Give the test an easy reference to the applet class under test
	@appletclass = self.class.appletclass or
		raise "No applet defined for '#{self.class.name}'. Add " +
			"'applet_under_test :<appletname>' to correct this."
	@appletname = self.class.appletname
	@action = nil
	
	@config = flexmock( "mock config" )
	@config.should_receive( :symbolize_keys ).and_return({})
	@config.should_receive( :member? ).
		with( :db ).
		and_return( false )
	@config.should_receive( :name ).and_return( "mock" )
	@config.should_receive( :member? ).
		with( :model ).
		and_return( false )
	@config.should_ignore_missing
	@template_factory = flexmock( "mock template factory" )

	@applet = @appletclass.new( @config, @template_factory, "#{@appletname}" )

	@delegate_behavior = nil
	@delegate_should_be_called = true
	@delegate_called = false
end

#setup_fixtured_request(action, *args) ⇒ Object

Set up faked request and transaction objects for the given action, using the given args as REST-style arguments, and/or query arguments if the last element is a Hash.



420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
# File 'lib/arrow/applettestcase.rb', line 420

def setup_fixtured_request( action, *args )
	uri = '/' + File.join( @appletname, action.to_s )
	req = Apache::Request.new( uri )

	params = args.last.is_a?( Hash ) ? args.pop : {}
	debug_msg "Parameters hash set to: %p" % [params]
	req.paramtable = params

	debug_msg "Request is: %p" % [req]
	#txn = Arrow::Transaction.new( req, @config, nil )
	txn = flexmock( "transaction" )
	txn.should_receive( :request ).
	    and_return( req ).zero_or_more_times
	txn.should_receive( :vargs= ).
	    with( Arrow::FormValidator ).zero_or_more_times
	
	vargs = flexmock( "form validator" )
	txn.should_receive( :vargs ).
		and_return( vargs ).
		zero_or_more_times
	vargs.should_receive( :[] ).zero_or_more_times
	
	debug_msg "Transaction is: %p" % [txn]
	return txn, req, vargs, *args
end

#should_delegate(&block) ⇒ Object

The default delegate block – call this from within your #with_fixtured_delegation block if the applet under test should delegate to the next applet in the chain.



391
392
393
394
395
# File 'lib/arrow/applettestcase.rb', line 391

def should_delegate( &block )
	@delegate_should_be_called = true
	@delegate_behavior = block || 
		method( :default_delegation_behavior ).to_proc
end

#should_load_template(key) {|mock_template| ... } ⇒ Object

Assert that the current action should load the template associated with the given key, and passes a mock template object to the given block for further specification.

Yields:

  • (mock_template)


450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
# File 'lib/arrow/applettestcase.rb', line 450

def should_load_template( key )
	tname = @applet.signature.templates[ key ] or
		raise Test::Unit::AssertionFailedError.new(
			"Expected applet to load the '#{key.inspect}' template\n" +
			"but there was no such template registered by the application." )
	
	mock_template = flexmock( "#{key.inspect} template")
	@template_factory.should_receive( :get_template ).
		with( tname ).and_return( mock_template ).at_least.once
	mock_template.should_ignore_missing

	yield( mock_template ) if block_given?
	
	return mock_template
end

#should_not_delegate(&block) ⇒ Object

Negated delegate block – call this at the end of your #with_fixtured_delegation block if the applet under test should not delegate to the next applet in the chain.



401
402
403
404
405
# File 'lib/arrow/applettestcase.rb', line 401

def should_not_delegate( &block )
	@delegate_should_be_called = false
	@delegate_behavior = block || 
		method( :default_delegation_behavior ).to_proc
end

#with_fixtured_action(action = nil, *args, &block) ⇒ Object

Set up faked request and transaction objects, yield to the given block with them, then run the applet under test with them when the block returns.



341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/arrow/applettestcase.rb', line 341

def with_fixtured_action( action=nil, *args, &block )
	@action = action
	txn, req, vargs, *args = setup_fixtured_request( action, *args )
	
	if block.arity == 3
		block.call( txn, req, vargs )
	else
		block.call( txn, req )
	end
	
	return @applet.run( txn, action.to_s, *args )
ensure
	@action = nil
end

#with_fixtured_delegation(chain = [], *args, &block) ⇒ Object

Set up a faked request and transaction object, yield to the given block with them, and then call the #delegate method of the applet under test. Unless otherwise indicated (via a call to #should_not_delegate), the expectation will be set up that the applet under test should call its delegate.



362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/arrow/applettestcase.rb', line 362

def with_fixtured_delegation( chain=[], *args, &block )
	txn, req, vargs, *args = setup_fixtured_request( "delegated_action", *args )

	# Set delegation expectation
	@delegate_behavior ||= should_delegate()

	if block.arity == 3
		block.call( txn, req, vargs )
	else
		block.call( txn, req )
	end
	
	rval = @applet.delegate( txn, chain, *args, &@delegate_behavior )
	
	if @delegate_should_be_called
		assert @delegate_called,
			"delegate applet was never called" 
	else
		assert !@delegate_called,
			"delegate applet was called unexpectedly"
	end

	return rval
end