testify 0.0.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ -c
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'classy'
4
+
5
+ group :development do
6
+ gem 'jeweler'
7
+ end
8
+
9
+ group :test do
10
+ gem 'rspec'
11
+ end
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 John Hyland and Emily Price
1
+ Copyright (c) 2010 John Hyland
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -2,6 +2,105 @@
2
2
 
3
3
  Testify is a test framework framework. Think "Rack for testing."
4
4
 
5
+ == Overview
6
+
7
+ Like Rack, Testify is typically going to be run as a stack of applications that
8
+ implement a +call+ method. In typical usage, the stack will be assembled by a
9
+ Runner and consist of a Framework and optionally some middleware, but this
10
+ isn't a requirement.
11
+
12
+ The source and detailed documentation can be found at
13
+ http://github.com/djspinmonkey/testify and
14
+ http://rdoc.info/projects/djspinmonkey/testify, respectively.
15
+
16
+ == Testify Applications
17
+
18
+ A Testify Application is any object that implements a +call+ method accepting a
19
+ single argument. The one argument will be a hash (canonically called env), and
20
+ the return value should be an array of TestResult objects.
21
+
22
+ === The +env+ Hash
23
+
24
+ The +env+ hash should be an instance of Hash with one of the following keys
25
+ defined:
26
+
27
+ [:files] an array of filenames containing the tests
28
+ [:path] a path containing the files containing the tests
29
+
30
+ In addition, all of the following keys should be defined:
31
+
32
+ [:testify_version] An array of integers representing this version of Testify
33
+ [:hooks] A hash of callbacks - see below.
34
+
35
+ Given that Testify is still at a fairly early stage of development, it is quite
36
+ possible that more required keys will be added later.
37
+
38
+ === Middleware
39
+
40
+ Middleware will generally either modify the env hash before the framework gets
41
+ it (eg, to execute only a subset of tests), or modify the TestResult objects on
42
+ the way back out (eg, to colorize the result text). If you want to respond to
43
+ a particular event as it happens (eg, send a Growl notification when a test
44
+ fails), it is probably best to do this by adding a hook rather than looking at
45
+ the returned TestResult objects, since call() will not return until all the
46
+ tests have finished running.
47
+
48
+ === Frameworks
49
+
50
+ A Framework object is responsible for actually running the tests and generating
51
+ TestResult objects. Frameworks are required to respect all of the hooks
52
+ described below. Generally, Frameworks should inherit from
53
+ Testify::Framework::Base and should have at least one alias in order to benefit
54
+ from all of Testify's built-in functionality, but neither of these is a
55
+ requirement. (See the Aliasable module in the Classy gem for a more thorough
56
+ description of aliases. http://github.com/djspinmonkey/classy)
57
+
58
+ At least initially, most Framework classes will likely be adaptors for existing
59
+ test frameworks (RSpec, Test::Unit, etc).
60
+
61
+ === Hooks
62
+
63
+ env[:hooks] is a hash containing arrays of callable objects (usually Procs,
64
+ but anything responding to +call+ will work) which will be called in specific
65
+ circumstances. The following keys should supported:
66
+
67
+ [:before_all] Will be called before running any tests. An array of Test
68
+ objects will be passed in. A middleware app might use this hook
69
+ instead of simply running immediately if it needed access to
70
+ individual tests instead of just files, or if it needed to run
71
+ after all other middleware. For example, if some other
72
+ middleware later in the stack were excluding some subset of tests
73
+ from running, you might need to be sure you were operating only
74
+ on the tests that would actually be run.
75
+ [:after_all] Will be called after running all tests. An array of TestResult
76
+ objects will be passed in. Middleware setting this hook should
77
+ probably just work with the array of TestResults returned from
78
+ calling the next Testify app on the stack instead, unless it
79
+ needs to run before the results pass through any other
80
+ middleware. Note that there is no guarantee another hook may not
81
+ be added prior to yours, however.
82
+ [:before_each] Will be called before running each individual test. A single
83
+ Test object will be passed in.
84
+ [:after_each] Will be called after running each individual test. A single
85
+ TestResult object will be passed in.
86
+ [:after_status] Points to a hash containing symbols as the keys, and arrays of
87
+ callable objects to be called whenever a TestResult is generated
88
+ with the status corresponding to that symbol. The TestResult
89
+ will be passed in.
90
+
91
+ If using the values provided by +Testify.env_defaults+, values for the first
92
+ four keys will be initialized to empty arrays, and +:after_status+ will be
93
+ initialized to an empty hash.
94
+
95
+ Like the +env+ hash, this is highly subject to change at this point.
96
+
97
+ == TODO
98
+
99
+ * Write adaptors for common test frameworks (rspec and minitest first, probably).
100
+ * Write an autotest-like Runner
101
+ * Write some useful middleware (growl notifications, colorized output, etc)
102
+ * World domination!
103
+
5
104
  == Note on Patches/Pull Requests
6
105
 
7
106
  * Fork the project.
@@ -15,4 +114,4 @@ Testify is a test framework framework. Think "Rack for testing."
15
114
 
16
115
  == Copyright
17
116
 
18
- Copyright (c) 2009 John Hyland and Emily Price. See LICENSE for details.
117
+ Copyright (c) 2010 John Hyland. See LICENSE for details.
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
+ require 'rspec/core/rake_task'
3
4
 
4
5
  begin
5
6
  require 'jeweler'
@@ -9,7 +10,8 @@ begin
9
10
  #gem.description = %Q{TODO: longer description of your gem}
10
11
  gem.email = "github@djspinmonkey.com"
11
12
  gem.homepage = "http://github.com/djspinmonkey/testify"
12
- gem.authors = ["John Hyland", "Emily Price"]
13
+ gem.authors = ["John Hyland"]
14
+ gem.add_dependency "classy", ">= 1.0.0"
13
15
  gem.add_development_dependency "rspec", ">= 1.2.9"
14
16
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
17
  end
@@ -18,28 +20,27 @@ rescue LoadError
18
20
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
21
  end
20
22
 
21
- require 'spec/rake/spectask'
22
- Spec::Rake::SpecTask.new(:spec) do |spec|
23
- spec.libs << 'lib' << 'spec'
24
- spec.spec_files = FileList['spec/**/*_spec.rb']
23
+ RSpec::Core::RakeTask.new do |t|
25
24
  end
26
25
 
27
- Spec::Rake::SpecTask.new(:rcov) do |spec|
28
- spec.libs << 'lib' << 'spec'
29
- spec.pattern = 'spec/**/*_spec.rb'
30
- spec.rcov = true
26
+ RSpec::Core::RakeTask.new(:coverage) do |t|
27
+ t.rcov = true
28
+ t.rcov_opts = ['--exclude', 'spec']
31
29
  end
32
30
 
33
- task :spec => :check_dependencies
31
+ task :spec
34
32
 
35
33
  task :default => :spec
36
34
 
37
- require 'rake/rdoctask'
38
- Rake::RDocTask.new do |rdoc|
39
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
-
41
- rdoc.rdoc_dir = 'rdoc'
42
- rdoc.title = "testify #{version}"
43
- rdoc.rdoc_files.include('README*')
44
- rdoc.rdoc_files.include('lib/**/*.rb')
45
- end
35
+ # This is generating some annoying warnings - commented out for now until I
36
+ # figure out how to quiet it down.
37
+ #
38
+ #require 'rdoc/task'
39
+ #RDoc::Task.new do |rdoc|
40
+ # version = File.exist?('VERSION') ? File.read('VERSION') : ""
41
+ #
42
+ # rdoc.rdoc_dir = 'rdoc'
43
+ # rdoc.title = "testify #{version}"
44
+ # rdoc.rdoc_files.include('README*')
45
+ # rdoc.rdoc_files.include('lib/**/*.rb')
46
+ #end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,115 @@
1
+ module Testify
2
+ # The module which all Framework classes are recommended to inherit.
3
+ # Framework provides a few utility methods and a means to keep track of all
4
+ # Framework classes (via Aliasable). Framework objects are Testify apps, so
5
+ # subclasses must implement a +call+ method as described in the README.rdoc
6
+ # file. It is also good practice to declare at least one alias (see the
7
+ # Aliasable module in the Classy gem for more details).
8
+ #
9
+ module Framework
10
+ include Aliasable
11
+
12
+ # The default set of statuses
13
+ #
14
+ DEFAULT_STATUSES = [ :passed, :pending, :failed, :error ]
15
+
16
+ # Extend the ClassMethods module and set the default statuses.
17
+ #
18
+ # :nodoc:
19
+ #
20
+ def self.included( klass )
21
+ klass.extend Testify::Framework::ClassMethods
22
+ klass.statuses *Testify::Framework::DEFAULT_STATUSES.dup
23
+ super
24
+ end
25
+
26
+ # Returns an array of absolute paths to each file defined by an +env+
27
+ # hash. The default implementation either returns the array of files, if
28
+ # :files is defined in the hash, or returns every file found in a
29
+ # traversal of the path in the :path key. Raises an exception if neither
30
+ # is defined, since that is not a valid +env+ hash.
31
+ #
32
+ # If a particular framework should only process files with names matching
33
+ # a particular glob pattern (eg, RSpec only wants files that match
34
+ # `*_spec.rb`), it can specify this with +file_pattern+.
35
+ #
36
+ def files( env )
37
+ return env[:files] if env.include? :files
38
+ raise(ArgumentError, "env hash must include either :files or :path") unless env.include? :path
39
+
40
+ file_glob = self.class.class_eval { @file_pattern } || '*'
41
+ Dir.glob(File.join(env[:path], '**', file_glob))
42
+ end
43
+
44
+ # Run all the appropriate hooks before running any tests at all.
45
+ #
46
+ def run_before_all_hooks( env )
47
+ env[:hooks][:before_all].each { |hook| hook.call }
48
+ end
49
+
50
+ # Run all the appropriate hooks before running each test.
51
+ #
52
+ def run_before_each_hooks( env )
53
+ env[:hooks][:before_each].each { |hook| hook.call }
54
+ end
55
+
56
+ # Run all the appropriate hooks after running all the tests
57
+ #
58
+ def run_after_all_hooks( env, results )
59
+ env[:hooks][:after_all].each { |hook| hook.call(results) }
60
+ end
61
+
62
+ # Run all the appropriate hooks after running each test individually
63
+ # (including the hooks for a particular status).
64
+ #
65
+ def run_after_each_hooks( env, result )
66
+ hooks = env[:hooks][:after_each]
67
+ hooks += env[:hooks][:after_status][result.status].to_a # .to_a in case it's nil
68
+ hooks.each { |hook| hook.call(result) }
69
+ end
70
+
71
+ module ClassMethods
72
+ # Gets and sets an array of symbols corresponding to the possible
73
+ # TestResult statuses that might be set by this Framework.
74
+ #
75
+ # For example, a framework with tests that can only pass or fail might
76
+ # like like this:
77
+ #
78
+ # class SomeFramework
79
+ # include Testify::Framework
80
+ #
81
+ # statuses :passed, :failed
82
+ #
83
+ # ...
84
+ # end
85
+ #
86
+ # SomeFramework.statuses # => [ :passed, :failed ]
87
+ #
88
+ def statuses( *new_statuses )
89
+ if new_statuses.any?
90
+ @statuses = new_statuses
91
+ end
92
+ @statuses
93
+ end
94
+
95
+ # Accepts a glob pattern that limits the paths returned by `#files`.
96
+ # Only paths with filenames that match this pattern will be returned by
97
+ # +.files+.
98
+ #
99
+ # For example, if all your framework's test files should end in
100
+ # _mytests.rb, you might do this:
101
+ #
102
+ # class MyTestFramework
103
+ # include Testify::Framework
104
+ #
105
+ # file_pattern '*_mytests.rb'
106
+ #
107
+ # ...
108
+ # end
109
+ #
110
+ def file_pattern( pattern )
111
+ self.class_eval { @file_pattern = pattern }
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,20 @@
1
+ module Testify
2
+ module Middleware
3
+ include Aliasable
4
+
5
+ # By default, Testify middleware is initialized with only the next app on
6
+ # the stack. If you override this, you may also need to override
7
+ # Runner.construct_app_stack.
8
+ #
9
+ def initialize(app)
10
+ @app = app
11
+ end
12
+
13
+ # By default, the middleware base class just calls the next app on the
14
+ # stack - you will almost certainly want to override this method.
15
+ #
16
+ def call(env)
17
+ @app.call(env)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,138 @@
1
+ module Testify
2
+
3
+ # Runner is a Testify app intended to sit at the top of a Testify stack and
4
+ # provide a simple mechanism for running a set of tests. If you're writing
5
+ # an autotest-like application, Runner is what you would use. Runner uses
6
+ # Templatable from the Classy gem, so you can either subclass Runner and use
7
+ # DSL-like class methods to define its behavior, or instantiate it directly
8
+ # and configure the instance.
9
+ #
10
+ # For example,
11
+ #
12
+ # class SampleRunner
13
+ # include Testify::Runner
14
+ #
15
+ # framework :rspec
16
+ # middleware :growl, :red_green
17
+ # end
18
+ #
19
+ # @runner = SampleRunner.new
20
+ #
21
+ # would be equivalent to
22
+ #
23
+ # @runner = Testify::Runner::Base.new
24
+ # @runner.framework = Testify::Framework::RspecAdaptor
25
+ # @runner.middleware = [Testify::Middleware::Growl, Testify::Middleware::RedGreen]
26
+ #
27
+ # Which approach is best depends on your needs, or you can use a mixture of
28
+ # the two.
29
+ #
30
+ module Runner
31
+ def self.included( klass )
32
+ klass.class_eval do
33
+ extend Templatable
34
+ extend Testify::Runner::ClassMethods
35
+
36
+ attr_accessor :status, :framework_instance, :test_results
37
+ templatable_attr :framework, :middleware
38
+
39
+ @@middleware = []
40
+ end
41
+ super
42
+ end
43
+
44
+ # Allows the framework to be specified on an instance of Runner. See +.framework+.
45
+ #
46
+ def framework= (fw)
47
+ @framework = Testify::Framework::Base.find(fw)
48
+ end
49
+
50
+ # Allows the middleware array to be specified for an instance of Runner. See +.middleware+.
51
+ #
52
+ # Note: The class method accepts a list, but the instance method must be passed an actual array.
53
+ #
54
+ # Example:
55
+ #
56
+ # runner = YourRunner.new
57
+ # runner.middleware = [:dots, :colorize]
58
+ #
59
+ def middleware=( middleware )
60
+ @middleware = middleware.map { |mw| Testify::Middleware.find(mw) }
61
+ end
62
+
63
+ # Constructs the stack of middleware and framework. If you are doing
64
+ # something unconventional (eg, creating middleware that requires
65
+ # additional initialization parameters), you may need to override this
66
+ # method.
67
+ #
68
+ def construct_app_stack
69
+ @framework_instance ||= framework.new
70
+ top_app = @framework_instance
71
+
72
+ middleware.each do |mw_class|
73
+ top_app = mw_class.new(top_app)
74
+ end
75
+
76
+ top_app
77
+ end
78
+
79
+ # Run the tests. Accepts a hash that will be merged in to the default env
80
+ # and passed to the Testify app stack.
81
+ #
82
+ def run( options = {} )
83
+ top_app = construct_app_stack
84
+
85
+ env = Testify.env_defaults.merge options
86
+ @test_results = top_app.call(env)
87
+
88
+ @status = @test_results.collect(&:status).max # XXX: Optimize?
89
+
90
+ @test_results
91
+ end
92
+
93
+ module ClassMethods
94
+ # Defines and/or returns the framework to use. This can be any class whose
95
+ # instances are Testify apps, and an instance of this class will be placed
96
+ # at the bottom of the Testify stack used by Runner#run. Typically, this
97
+ # will probably be a test framework built on Testify or (more likely, at
98
+ # least for now) a Testify adaptor for some existing framework. If the
99
+ # class descends from Testify::Framework::Base and defines an alias (eg,
100
+ # aka :rspec), you may use that alias instead of passing in the actual
101
+ # class.
102
+ #
103
+ # Example:
104
+ #
105
+ # class YourRunner < Testify::Runner::Base
106
+ # framework :rspec
107
+ # end
108
+ # @runner = YourRunner.new
109
+ # @runner.framework # <= Testify::Framework::RspecAdaptor
110
+ #
111
+ # Note that you can also specify the framework on an instance, using
112
+ # +#framework=+.
113
+ #
114
+ # @runner = Testify::Runner.new
115
+ # @runner.framework YourAwesomeTestifyFrameworkClass
116
+ #
117
+ def framework( fw = nil )
118
+ self.framework = Testify::Framework.find(fw) if fw
119
+ self.send(:class_variable_get, :@@framework)
120
+ end
121
+
122
+ # Defines and/or returns the middleware to use, in the same manner as
123
+ # +.framework+. Note that this completely replaces any previous
124
+ # middleware specified for this class.
125
+ #
126
+ # Example:
127
+ #
128
+ # class YourRunner < Testify::Runner::Base
129
+ # middleware :dots, :colorize
130
+ # end
131
+ #
132
+ def middleware( *middlewares )
133
+ self.middleware = middlewares.map { |mw| Testify::Middleware.find(mw) } unless middlewares.empty?
134
+ self.send(:class_variable_get, :@@middleware)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,18 @@
1
+ module Testify
2
+
3
+ # A TestResult object represents the results of running a single test,
4
+ # whatever that means in the context of your framework.
5
+ #
6
+ class TestResult
7
+ attr_accessor :status, :message, :file, :line
8
+
9
+ def initialize (options = {})
10
+ @status = options[:status]
11
+ @message = options[:message]
12
+ @file = options[:file]
13
+ @line = options[:line]
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,41 @@
1
+ # gems
2
+ require 'classy'
3
+
4
+ # Make sure we're grabbing the right version of everything.
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+
7
+ require 'framework'
8
+ require 'runner'
9
+ require 'middleware'
10
+ require 'test_result'
11
+
12
+ # Put the LOAD_PATH back the way it was.
13
+ $LOAD_PATH.shift
14
+
15
+ # The Testify module provides some general-purpose functions that aren't
16
+ # specific to any single component.
17
+ module Testify
18
+
19
+ # Provides some reasonable defaults to create a new env hash. This method
20
+ # does not fill in :path or :files, so you'll need to explicitly specify one
21
+ # or the other.
22
+ #
23
+ def self.env_defaults
24
+ { :testify_version => Testify.version,
25
+ :hooks => { :before_all => [],
26
+ :after_all => [],
27
+ :before_each => [],
28
+ :after_each => [],
29
+ :after_status => {},
30
+ }
31
+ }
32
+ end
33
+
34
+ # Returns the current version as an array. (Eg, version 1.2.3 would be
35
+ # returned as [1, 2, 3].)
36
+ #
37
+ def self.version
38
+ File.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).chomp.split('.').collect { |n| n.to_i }
39
+ end
40
+
41
+ end
@@ -0,0 +1,9 @@
1
+ This is a sample Testify-based setup. The framework is called (imaginatively
2
+ enough) AwesomeTestFramework - this is the same level as RSpec or minitest.
3
+ The runner is called AwesomeRunner - think of the 'rspec' command or
4
+ 'autotest'. And for flavor we've added some middleware to output valuable
5
+ information while the tests run and append some footer text at the end.
6
+
7
+ To see it in action, just run this:
8
+
9
+ ./awesome_runner.rb awesome_tests
@@ -0,0 +1,28 @@
1
+ require_relative '../lib/testify'
2
+ require_relative 'awesome_test_framework'
3
+ require_relative 'dots'
4
+ require_relative 'summary'
5
+
6
+ class AwesomeRunner
7
+ include Testify::Runner
8
+
9
+ # Specify the framework we want to use. This alias is defined in
10
+ # AwesomeTestFramework.
11
+ #
12
+ framework :awesome
13
+
14
+ # Specify the middleware. Again, this is defined in RadMiddleware.
15
+ #
16
+ middleware :summary, :dots
17
+ end
18
+
19
+ # Instantiate the runner and run the tests.
20
+ #
21
+ tests_path = File.join(File.dirname(__FILE__), 'awesome_tests')
22
+ @runner = AwesomeRunner.new
23
+ results = @runner.run(:path => tests_path)
24
+
25
+ #puts "#{results.size} tests run"
26
+ #results.each do |res|
27
+ # puts "#{res.status} - #{res.message}"
28
+ #end
@@ -0,0 +1,51 @@
1
+ # A sample test framework built using Testify.
2
+ #
3
+ class AwesomeTestFramework
4
+ include Testify::Framework
5
+
6
+ # Specify an alias for this framework.
7
+ #
8
+ aka :awesome
9
+
10
+ # These are the statuses this framework might return, in increasing order of severity.
11
+ #
12
+ statuses :pass, :fail, :error
13
+
14
+ # This is what a test file for this framework looks like.
15
+ #
16
+ file_pattern '*.aws'
17
+
18
+ # This is where you do your thing. In a real test framework, you'd probably
19
+ # be testing assertions and setting up contexts and generally making the
20
+ # magic happen here (or at least calling the libraries where the magic
21
+ # happens).
22
+ #
23
+ # In this example, we're just reading test results out of a file and calling
24
+ # the appropriate hooks.
25
+ #
26
+ def call( env )
27
+ results = []
28
+
29
+ run_before_all_hooks(env)
30
+
31
+ files(env).each do |file|
32
+ open file do |f|
33
+ f.lines.each_with_index do |line, line_number|
34
+ run_before_each_hooks(env)
35
+
36
+ message, status = * line.split(':')
37
+ status = status.strip.to_sym
38
+ result = Testify::TestResult.new( :file => file, :line => line_number, :message => message, :status => status )
39
+ results << result
40
+
41
+ run_after_each_hooks(env, result)
42
+ end
43
+ end
44
+ end
45
+
46
+ run_after_all_hooks(env, results)
47
+
48
+ results
49
+ end
50
+
51
+ end
@@ -0,0 +1,3 @@
1
+ five should be an integer: pass
2
+ love should be in the air: pass
3
+ garble barf: asplode
@@ -0,0 +1,3 @@
1
+ axioms should not be testable: pass
2
+ goods should be fungible: pass
3
+ this framework should be awesome: fail
@@ -0,0 +1,3 @@
1
+ true should be true: pass
2
+ 1 + 1 should equal 2: pass
3
+ this test should pass: pass
@@ -0,0 +1,31 @@
1
+ # Output a dot every time a test passes, an F when it fails, or a E on an
2
+ # error. On any other status, it prints a ?.
3
+ #
4
+ class Dots
5
+ include Testify::Middleware
6
+
7
+ aka :dots
8
+
9
+ def call( env )
10
+ # Output dots (et al) as each test runs.
11
+ #
12
+ env[:hooks][:after_each].push(lambda do |result|
13
+ case result.status
14
+ when :pass
15
+ print '.'
16
+ when :fail
17
+ print 'F'
18
+ when :error
19
+ print 'E'
20
+ else
21
+ print '?'
22
+ end
23
+ end)
24
+
25
+ # Output a couple newlines at the end after all the tests have run.
26
+ #
27
+ env[:hooks][:after_all].push(lambda { |ignored| puts; puts })
28
+
29
+ @app.call(env)
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ # Output a summary of the test results after they've all been run.
2
+ #
3
+ class Summary
4
+ include Testify::Middleware
5
+
6
+ aka :summary
7
+
8
+ def call( env )
9
+ results = @app.call(env)
10
+
11
+ summary = Hash.new(0)
12
+ results.each { |res| summary[res.status] += 1 }
13
+
14
+ puts "Ran #{results.size} tests in all"
15
+ summary.each { |status, count| puts "#{count} tests with status #{status}" }
16
+
17
+ results
18
+ end
19
+ end
@@ -0,0 +1,81 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe "Testify::Framework" do
4
+
5
+ before :all do
6
+ Testify::Framework.forget_aliases
7
+
8
+ destroy_class :VanillaFramework
9
+ class VanillaFramework
10
+ include Testify::Framework
11
+ aka :vanilla
12
+ end
13
+ @framework = VanillaFramework.new
14
+ end
15
+
16
+ it "should be able to specify known aliases" do
17
+ Testify::Framework.find(:vanilla).should equal VanillaFramework
18
+ end
19
+
20
+ it "should raise an ArgumentError if a given alias is already taken" do
21
+ lambda {
22
+ class ThisIsTheRepeat
23
+ include Testify::Framework
24
+ aka :vanilla
25
+ end
26
+ }.should raise_error(ArgumentError)
27
+
28
+ end
29
+
30
+ it "should be able to specify a status ranking without affecting other Frameworks" do
31
+ class ChangedFramework
32
+ include Testify::Framework
33
+ statuses :passed, :failed, :exploded
34
+ end
35
+
36
+ VanillaFramework.statuses.should eql Testify::Framework::DEFAULT_STATUSES
37
+ ChangedFramework.statuses.should eql [ :passed, :failed, :exploded ]
38
+ end
39
+
40
+ context '#files' do
41
+ before do
42
+ @test_path = File.join(File.dirname(__FILE__), "sample_tests")
43
+ all_files = ['failing_tests', 'mixed_tests', 'passing_tests', 'test_helper']
44
+ test_files = all_files - ['test_helper']
45
+ @all_paths = all_files.collect { |f| File.join(File.dirname(__FILE__), 'sample_tests', f) }
46
+ @test_paths = test_files.collect { |f| File.join(File.dirname(__FILE__), 'sample_tests', f) }
47
+ end
48
+
49
+ after do
50
+ VanillaFramework.file_pattern nil
51
+ end
52
+
53
+ it "should find files specified by :path" do
54
+ env = { :path => @test_path }
55
+ @framework.files(env).sort.should eql @all_paths
56
+ end
57
+
58
+ it "should find files specified by :files" do
59
+ env = { :files => @all_paths }
60
+ @framework.files(env).sort.should eql @all_paths
61
+ end
62
+
63
+ it "should raise an ArgumentError if neither :files nor :path is defined" do
64
+ env = {}
65
+ lambda {
66
+ @framework.files(env)
67
+ }.should raise_exception ArgumentError
68
+ end
69
+
70
+ it "should respect the .file_pattern setting" do
71
+ class VanillaFramework
72
+ file_pattern '*_tests'
73
+ end
74
+
75
+ env = { :path => @test_path }
76
+ @framework.files(env).sort.should eql @test_paths
77
+ end
78
+ end
79
+
80
+ end
81
+
@@ -0,0 +1,5 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe "Testify::Middleware::Base" do
4
+ # It doesn't actually do anything that doesn't come from a module.
5
+ end
@@ -0,0 +1,101 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe "Testify::Runner" do
4
+
5
+ before :each do
6
+ Testify::Middleware.forget_aliases
7
+ Testify::Framework.forget_aliases
8
+
9
+ destroy_class :SomeMiddleware
10
+ class SomeMiddleware
11
+ include Testify::Middleware
12
+ aka :some_middleware
13
+ end
14
+
15
+ destroy_class :SomeMoreMiddleware
16
+ class SomeMoreMiddleware
17
+ include Testify::Middleware
18
+ aka :more_middleware
19
+ end
20
+
21
+ destroy_class :SomeTestFramework
22
+ class SomeTestFramework
23
+ include Testify::Framework
24
+
25
+ aka :some_test_framework
26
+ attr_accessor :env
27
+
28
+ def call (env)
29
+ @env = env
30
+ [Testify::TestResult.new(:message => "Testing", :status => :pass)]
31
+ end
32
+ end
33
+
34
+ destroy_class :TestRunner
35
+ class TestRunner
36
+ include Testify::Runner
37
+ end
38
+ @runner = TestRunner.new
39
+
40
+ @test_path = File.expand_path(File.join(File.dirname(__FILE__), 'sample_tests'))
41
+ end
42
+
43
+ it "should be able to specify a test framework by alias" do
44
+ class TestRunner
45
+ framework :some_test_framework
46
+ end
47
+
48
+ TestRunner.framework.should eql SomeTestFramework
49
+ @runner.framework.should eql SomeTestFramework
50
+ end
51
+
52
+ it "should be able to specify a test framework by class" do
53
+ class TestRunner
54
+ framework SomeTestFramework
55
+ end
56
+
57
+ TestRunner.framework.should eql SomeTestFramework
58
+ @runner.framework.should eql SomeTestFramework
59
+ end
60
+
61
+ it "should be able to specify middleware by alias" do
62
+ class TestRunner
63
+ middleware :some_middleware, :more_middleware
64
+ end
65
+
66
+ TestRunner.middleware.should eql [SomeMiddleware, SomeMoreMiddleware]
67
+ @runner.middleware.should eql [SomeMiddleware, SomeMoreMiddleware]
68
+ end
69
+
70
+ context "#run" do
71
+ it "should accept options and put them in the env hash" do
72
+ framework = SomeTestFramework.new
73
+ @runner.framework_instance = framework
74
+ @runner.run :foo => 'bar'
75
+ framework.env[:foo].should == 'bar'
76
+ end
77
+ end
78
+
79
+ context "just created" do
80
+ it "should have a nil status" do
81
+ @runner.status.should be_nil
82
+ end
83
+ end
84
+
85
+ context "with a test framework defined" do
86
+ before do
87
+ @runner.framework = SampleFramework
88
+ end
89
+
90
+ context "after running" do
91
+ before do
92
+ test_path = File.join(File.dirname(__FILE__), "sample_tests")
93
+ @runner.run :path => test_path
94
+ end
95
+
96
+ it "should have a status" do
97
+ @runner.status.should_not be_nil
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,33 @@
1
+ # A sample Testify framework intended to be used while testing Testify. A
2
+ # "test" consists of a line in the file, which should contain the status
3
+ # and optionally a colon and a message.
4
+ #
5
+ # Example test file:
6
+ # fail:The thing is not in the place.
7
+ # success
8
+ # pending:Need to implement cat napkins.
9
+ # success
10
+ #
11
+ class SampleFramework
12
+ include Testify::Framework
13
+
14
+ aka :sample
15
+
16
+ # Run the tests.
17
+ #
18
+ def call (env)
19
+ results = []
20
+
21
+ files(env).each do |file|
22
+ line_number = 0
23
+ File.open(file).each_line do |line|
24
+ line_number += 1
25
+ (status, message) = line.split(':')
26
+ results.push Testify::TestResult.new(:status => status.to_sym, :message => message, :file => file, :line => line_number)
27
+ end
28
+ end
29
+
30
+ results
31
+ end
32
+
33
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe "Something broken" do
4
+ it "should fail" do
5
+ true.should be false
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Something that sorta works but is broken in parts" do
4
+ it "should pass some things" do
5
+ true.should be true
6
+ end
7
+
8
+ it "should fail others" do
9
+ "foo".should be "bar"
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Something that works" do
4
+ it "should pass" do
5
+ true.should be true
6
+ end
7
+
8
+ it "should still pass" do
9
+ "foo".should == "foo"
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec'
2
+ require 'spec/autorun'
3
+
4
+ Spec::Runner.configure do |config|
5
+ end
@@ -1,9 +1,14 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
- require 'testify'
4
- require 'spec'
5
- require 'spec/autorun'
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'testify')
2
+ require_relative '../lib/testify'
3
+ require_relative 'sample_framework'
4
+ require 'rspec'
6
5
 
7
- Spec::Runner.configure do |config|
8
-
6
+ RSpec.configure do |config|
7
+ end
8
+
9
+ # Doesn't work for anything inside a module (eg, if you try to remove
10
+ # Testify::Framework::RSpec this will totally blow up).
11
+ def destroy_class ( klass )
12
+ klass = klass.name.to_s if klass.kind_of? Class
13
+ Object.class_exec { remove_const klass } if Object.const_defined? klass
9
14
  end
@@ -0,0 +1,11 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe "Testify::TestResult" do
4
+ it "should be instantializable with a hash of attributes" do
5
+ res = Testify::TestResult.new(:file => 'woo_tests.rb', :line => 42, :status => :error, :message => "Your test asploded.")
6
+ res.status.should == :error
7
+ res.message.should == "Your test asploded."
8
+ res.file.should == 'woo_tests.rb'
9
+ res.line.should == 42
10
+ end
11
+ end
@@ -1,7 +1,23 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
2
 
3
3
  describe "Testify" do
4
- it "fails" do
5
- fail "hey buddy, you should probably rename this file and start specing for real"
4
+
5
+ context '.env_defaults' do
6
+ it "should provide an env hash with some default values" do
7
+ defaults = Testify.env_defaults
8
+
9
+ # Verify the values on these ones
10
+ defaults[:testify_version].should == Testify.version
11
+ defaults[:hooks].should == { :before_all => [], :after_all => [], :before_each => [], :after_each => [], :after_status => {} }
12
+ end
6
13
  end
14
+
15
+ context '.version' do
16
+ it "should return an array of three integers" do
17
+ version = Testify.version
18
+ version.should have(3).parts
19
+ version.each { |n| n.should be_an Integer }
20
+ end
21
+ end
22
+
7
23
  end
metadata CHANGED
@@ -1,28 +1,61 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: testify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ prerelease:
5
+ version: 1.0.0
5
6
  platform: ruby
6
7
  authors:
7
8
  - John Hyland
8
- - Emily Price
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-11-02 00:00:00 -05:00
14
- default_executable:
13
+ date: 2011-10-31 00:00:00 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
- name: rspec
16
+ name: classy
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: jeweler
28
+ requirement: &id002 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
18
34
  type: :development
19
- version_requirement:
20
- version_requirements: !ruby/object:Gem::Requirement
35
+ prerelease: false
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: classy
39
+ requirement: &id003 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 1.0.0
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
49
+ name: rspec
50
+ requirement: &id004 !ruby/object:Gem::Requirement
51
+ none: false
21
52
  requirements:
22
53
  - - ">="
23
54
  - !ruby/object:Gem::Version
24
55
  version: 1.2.9
25
- version:
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *id004
26
59
  description:
27
60
  email: github@djspinmonkey.com
28
61
  executables: []
@@ -34,42 +67,62 @@ extra_rdoc_files:
34
67
  - README.rdoc
35
68
  files:
36
69
  - .document
37
- - .gitignore
70
+ - .rspec
71
+ - Gemfile
38
72
  - LICENSE
39
73
  - README.rdoc
40
74
  - Rakefile
75
+ - VERSION
76
+ - lib/framework.rb
77
+ - lib/middleware.rb
78
+ - lib/runner.rb
79
+ - lib/test_result.rb
41
80
  - lib/testify.rb
42
- - spec/spec.opts
81
+ - samples/README
82
+ - samples/awesome_runner.rb
83
+ - samples/awesome_test_framework.rb
84
+ - samples/awesome_tests/asplode.aws
85
+ - samples/awesome_tests/failing.aws
86
+ - samples/awesome_tests/passing.aws
87
+ - samples/dots.rb
88
+ - samples/summary.rb
89
+ - spec/framework_spec.rb
90
+ - spec/middleware_spec.rb
91
+ - spec/runner_spec.rb
92
+ - spec/sample_framework.rb
93
+ - spec/sample_tests/failing_tests
94
+ - spec/sample_tests/mixed_tests
95
+ - spec/sample_tests/passing_tests
96
+ - spec/sample_tests/test_helper
43
97
  - spec/spec_helper.rb
98
+ - spec/test_result_spec.rb
44
99
  - spec/testify_spec.rb
45
- has_rdoc: true
46
100
  homepage: http://github.com/djspinmonkey/testify
47
101
  licenses: []
48
102
 
49
103
  post_install_message:
50
- rdoc_options:
51
- - --charset=UTF-8
104
+ rdoc_options: []
105
+
52
106
  require_paths:
53
107
  - lib
54
108
  required_ruby_version: !ruby/object:Gem::Requirement
109
+ none: false
55
110
  requirements:
56
111
  - - ">="
57
112
  - !ruby/object:Gem::Version
58
113
  version: "0"
59
- version:
60
114
  required_rubygems_version: !ruby/object:Gem::Requirement
115
+ none: false
61
116
  requirements:
62
117
  - - ">="
63
118
  - !ruby/object:Gem::Version
64
119
  version: "0"
65
- version:
66
120
  requirements: []
67
121
 
68
122
  rubyforge_project:
69
- rubygems_version: 1.3.5
123
+ rubygems_version: 1.8.6
70
124
  signing_key:
71
125
  specification_version: 3
72
126
  summary: Testify is a test framework framework. Think "Rack for testing."
73
- test_files:
74
- - spec/spec_helper.rb
75
- - spec/testify_spec.rb
127
+ test_files: []
128
+
data/.gitignore DELETED
@@ -1,21 +0,0 @@
1
- ## MAC OS
2
- .DS_Store
3
-
4
- ## TEXTMATE
5
- *.tmproj
6
- tmtags
7
-
8
- ## EMACS
9
- *~
10
- \#*
11
- .\#*
12
-
13
- ## VIM
14
- *.swp
15
-
16
- ## PROJECT::GENERAL
17
- coverage
18
- rdoc
19
- pkg
20
-
21
- ## PROJECT::SPECIFIC
@@ -1 +0,0 @@
1
- --color