Skip to content

Instantly share code, notes, and snippets.

@jarib
Created August 19, 2009 02:04
Show Gist options
  • Save jarib/170109 to your computer and use it in GitHub Desktop.
Save jarib/170109 to your computer and use it in GitHub Desktop.
#
# simpletester.rb
#
# Created by Jari Bakken on 2009-08-19.
# Copyright 2009 Jari Bakken. All rights reserved.
#
require "rubygems"
require "json"
require "erb"
module QA
module SimpleTester
class AbstractFormatter
def begin; end
def begin_object(o); end
def begin_assert(e, o); end
def assertion(a); end
def finish_assert; end
def finish_object; end
def finish; end
end
class TextFormatter < AbstractFormatter
def begin(expectations)
$stdout.sync = true
end
def begin_object(obj)
@object = obj
@indent = "\t"
end
def begin_assert(expectation, obj)
@expectation = expectation
print "#{@indent}. "
end
def finish_object
@indent = ""
puts(@failed ? "[ FAIL ] #{@object}" : "[ PASS ] #{@object}")
end
def assertion(assertion)
@assertion_counter += 1
if assertion.failed?
@failure_counter += 1
@failed = true
puts "#{@indent}not ok! #{@expectation.description}. (#{assertion.expected.inspect} != #{assertion.actual.inspect})"
else
puts "#{@indent}ok #{@expectation.description}."
end
end
end
class JsonFormatter < AbstractFormatter
def begin(*args)
@result = {}
end
def begin_object(o)
@object = o
@result[o] = []
end
def begin_assert(e, o)
end
def assertion(a)
@result[@object] << a
end
def finish_assert; end
def finish_object; end
def finish
puts @result.to_json
end
end
class PjsonFormatter < JsonFormatter
def finish; puts JSON.pretty_generate(@result); end
end
class HtmlFormatter < AbstractFormatter
def begin(expectations)
puts ERB.new(<<-HTML, 0, "%-<>").result(binding)
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>QA::SimpleTester</title>
<style type="text/css" media="screen">
.pass { background-color: green;}
.fail { background-color: red;}
</style>
</head>
<body>
<table border="0">
<tr>
<th>Resource</th>
<%- expectations.each do |exp| -%>
<th><%= exp.description %></th>
<%- end -%>
</tr>
HTML
end
def begin_object(o)
puts "<tr><td>%s</td>" % escape_html(o.to_s)
end
def begin_assert(e, o); end
def assertion(a)
if a.failed?
class_name = 'fail'
content = "<em>expected:</em> %s<hr><em>actual:</em> %s" % [a.expected, a.actual].map { |e| escape_html(e.inspect) }
else
class_name = 'pass'
content = escape_html(a.actual.inspect)
end
puts '<td class="%s">%s</td>' % [class_name, content]
end
def finish_assert; end
def finish_object
puts "</tr>"
end
def finish
puts <<-HTML
</table>
</body>
</html>
HTML
end
private
def escape_html(str)
str.gsub("<", '&lt;').gsub(">", '&gt;')
end
end
class Expectation
attr_reader :description, :expected, :actual
def initialize(description)
@description = description
end
def block=(blk)
if @block
raise "tried to assign block to alrady defined expectation, make sure each expectation has a description"
end
@block = blk
self
end
def to_be(obj)
@expected = obj
end
def test(obj)
@actual = @block.call(obj)
@failed = @expected != @actual
self
end
def failed?
@failed == true
end
def to_json(*args)
{
:expected => @expected,
:actual => @actual,
:failed => @failed,
:description => @description
}.to_json(*args)
end
end
class << self
def set(key, value)
options[key] = value
end
def run!
validate_data_source
validate_formatter
test_expectations
end
def desc(str)
expectations << (e = Expectation.new(str))
self
end
def expect(&blk)
e = expectations.last
e.block = blk
e
end
private
def validate_data_source
unless options[:data_source]
raise "no :data_source"
end
unless options[:data_source].respond_to?(:each)
raise "invalid :data_source (must respond to `each')"
end
end
def validate_formatter
formatter_name = "#{options[:output_format].to_s.capitalize}Formatter"
@formatter = const_get(formatter_name).new
rescue NameError
raise "invalid :output_format (#{options[:output_format].inspect}) - #{self.name}::#{formatter_name} not found"
end
def options
@options ||= default_options
end
def expectations
@expectations ||= []
end
def results
@results ||= {}
end
def test_expectations
@formatter.begin(expectations)
options[:data_source].each do |obj|
@formatter.begin_object(obj)
run_expectations_with obj
@formatter.finish_object
end
@formatter.finish
end
def run_expectations_with(obj)
results[obj] = expectations.map do |expectation|
@formatter.begin_assert(expectation, obj)
@formatter.assertion expectation.test(obj)
@formatter.finish_assert
end
end
def default_options
{
:output_format => :text
}
end
end
module Delegator
def set(key, value); QA::SimpleTester.set(key, value); end
def expect(&blk); QA::SimpleTester.expect(&blk); end
def desc(string); QA::SimpleTester.desc(string); end
end
end
at_exit { QA::SimpleTester.run! }
end
require "ostruct"
class DummyResourceFinder
def initialize(args)
end
def each(&blk)
data = [
OpenStruct.new(:path => "/foo/bar", :content_type => "application/xml", :well_formed? => true, :content_encoding => "UTF-8", :xml_declaration_encoding => "UTF-8"),
OpenStruct.new(:path => "/bar/baz", :content_type => "application/f.xml", :well_formed? => true, :content_encoding => "UTF-8", :xml_declaration_encoding => "ISO-8859-1"),
]
data.each do |item|
item.instance_eval do
def to_s
"<RestResource #{path}>"
end
end
yield item
end
end
end
# test script
include QA::SimpleTester::Delegator
set :data_source , DummyResourceFinder.new(ARGV) # must repond to each
set :output_format, :html
desc 'XML has correct Content-Type'
expect { |rest| rest.content_type }.to_be 'application/xml'
desc 'XML is well formed'
expect { |rest| rest.well_formed? }.to_be true
desc 'XML has correct Content-Encoding'
expect { |rest| rest.content_encoding }.to_be "UTF-8"
desc 'XML has correct XML declaration encoding'
expect { |rest| rest.xml_declaration_encoding }.to_be "UTF-8"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment