assert 0.1.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/.gitignore +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +17 -0
- data/README.rdoc +77 -0
- data/Rakefile +7 -0
- data/assert.gemspec +21 -0
- data/examples/empty_test.rb +5 -0
- data/examples/results_test.rb +25 -0
- data/examples/single_test.rb +9 -0
- data/lib/assert.rb +8 -0
- data/lib/assert/assertions.rb +253 -0
- data/lib/assert/context.rb +196 -0
- data/lib/assert/options.rb +43 -0
- data/lib/assert/rake_tasks.rb +95 -0
- data/lib/assert/result.rb +164 -0
- data/lib/assert/result_set.rb +14 -0
- data/lib/assert/runner.rb +60 -0
- data/lib/assert/setup/autorun.rb +34 -0
- data/lib/assert/setup/helpers.rb +62 -0
- data/lib/assert/setup/suite.rb +12 -0
- data/lib/assert/setup/view.rb +11 -0
- data/lib/assert/suite.rb +128 -0
- data/lib/assert/test.rb +90 -0
- data/lib/assert/version.rb +3 -0
- data/lib/assert/view/base.rb +54 -0
- data/lib/assert/view/terminal.rb +138 -0
- data/test/assertions/assert_block_test.rb +39 -0
- data/test/assertions/assert_instance_of_test.rb +43 -0
- data/test/assertions/assert_kind_of_test.rb +43 -0
- data/test/assertions/assert_not_block_test.rb +39 -0
- data/test/assertions/assert_not_instance_of_test.rb +43 -0
- data/test/assertions/assert_not_kind_of_test.rb +43 -0
- data/test/assertions/assert_not_respond_to_test.rb +43 -0
- data/test/assertions/assert_nothing_raised_test.rb +46 -0
- data/test/assertions/assert_raises_test.rb +49 -0
- data/test/assertions/assert_respond_to_test.rb +43 -0
- data/test/assertions_test.rb +334 -0
- data/test/context/class_methods_test.rb +314 -0
- data/test/context_test.rb +288 -0
- data/test/fixtures/inherited_stuff.rb +36 -0
- data/test/fixtures/sample_context.rb +13 -0
- data/test/helper.rb +52 -0
- data/test/irb.rb +10 -0
- data/test/options_test.rb +78 -0
- data/test/result_set_test.rb +89 -0
- data/test/result_test.rb +255 -0
- data/test/runner_test.rb +33 -0
- data/test/suite_test.rb +200 -0
- data/test/test/running_test.rb +327 -0
- data/test/test_test.rb +184 -0
- data/test/view_test.rb +35 -0
- metadata +155 -0
@@ -0,0 +1,196 @@
|
|
1
|
+
require 'assert/suite'
|
2
|
+
require 'assert/assertions'
|
3
|
+
require 'assert/result'
|
4
|
+
|
5
|
+
module Assert
|
6
|
+
class Context
|
7
|
+
include Assert::Assertions
|
8
|
+
|
9
|
+
# a Context is a scope for tests to run in. Contexts have setup and
|
10
|
+
# teardown blocks, subjects, and descriptions. Tests are run in the
|
11
|
+
# scope of a Context instance. Therefore, a Context should have
|
12
|
+
# minimal base logic/methods/instance_vars. The instance should remain
|
13
|
+
# pure to not pollute test scopes.
|
14
|
+
|
15
|
+
# if a class subclasses Context, add it to the suite
|
16
|
+
def self.inherited(klass)
|
17
|
+
Assert.suite << klass
|
18
|
+
end
|
19
|
+
|
20
|
+
# put all logic here to keep context instances pure for running tests
|
21
|
+
class << self
|
22
|
+
attr_accessor :subject_block
|
23
|
+
|
24
|
+
def setup_once(&block)
|
25
|
+
Assert.suite.setup(&block)
|
26
|
+
end
|
27
|
+
alias_method :before_once, :setup_once
|
28
|
+
|
29
|
+
def teardown_once(&block)
|
30
|
+
Assert.suite.teardown(&block)
|
31
|
+
end
|
32
|
+
alias_method :after_once, :teardown_once
|
33
|
+
|
34
|
+
def setup(&block)
|
35
|
+
raise ArgumentError, "please provide a setup block" unless block_given?
|
36
|
+
self.setup_blocks << block
|
37
|
+
end
|
38
|
+
alias_method :before, :setup
|
39
|
+
|
40
|
+
def teardown(&block)
|
41
|
+
raise ArgumentError, "please provide a teardown block" unless block_given?
|
42
|
+
self.teardown_blocks << block
|
43
|
+
end
|
44
|
+
alias_method :after, :teardown
|
45
|
+
|
46
|
+
def setup_blocks
|
47
|
+
@setup_blocks ||= []
|
48
|
+
end
|
49
|
+
|
50
|
+
def teardown_blocks
|
51
|
+
@teardown_blocks ||= []
|
52
|
+
end
|
53
|
+
|
54
|
+
def all_setup_blocks
|
55
|
+
inherited_blocks = if superclass.respond_to?(:all_setup_blocks)
|
56
|
+
superclass.all_setup_blocks
|
57
|
+
end
|
58
|
+
(inherited_blocks || []) + self.setup_blocks
|
59
|
+
end
|
60
|
+
|
61
|
+
def all_teardown_blocks
|
62
|
+
inherited_blocks = if superclass.respond_to?(:all_teardown_blocks)
|
63
|
+
superclass.all_teardown_blocks
|
64
|
+
end
|
65
|
+
(inherited_blocks || []) + self.teardown_blocks
|
66
|
+
end
|
67
|
+
|
68
|
+
def desc(text)
|
69
|
+
raise ArgumentError, "no context description provided" if text.nil?
|
70
|
+
self.descriptions << text
|
71
|
+
end
|
72
|
+
alias_method :description, :desc
|
73
|
+
|
74
|
+
def descriptions
|
75
|
+
@descriptions ||= []
|
76
|
+
end
|
77
|
+
|
78
|
+
def full_description
|
79
|
+
inherited_description = if superclass.respond_to?(:full_description)
|
80
|
+
superclass.full_description
|
81
|
+
end
|
82
|
+
parts = [ inherited_description ].push(self.descriptions).flatten.reject do |part|
|
83
|
+
!part || part.to_s.empty?
|
84
|
+
end
|
85
|
+
parts.join(" ") if !parts.empty?
|
86
|
+
end
|
87
|
+
|
88
|
+
def subject(&block)
|
89
|
+
raise ArgumentError, "please provide a subject block" unless block_given?
|
90
|
+
self.subject_block = block
|
91
|
+
end
|
92
|
+
|
93
|
+
def subject_block
|
94
|
+
@subject_block ||= if superclass.respond_to?(:subject_block)
|
95
|
+
superclass.subject_block
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def should(desc, &block)
|
100
|
+
raise ArgumentError, "please provide a test block" unless block_given?
|
101
|
+
method_name = "test: should #{desc}"
|
102
|
+
if method_defined?(method_name)
|
103
|
+
from = caller.first
|
104
|
+
puts "WARNING: should #{desc.inspect} is redefining #{method_name}!"
|
105
|
+
puts " from: #{from}"
|
106
|
+
end
|
107
|
+
define_method(method_name, &block)
|
108
|
+
end
|
109
|
+
|
110
|
+
def should_eventually(desc, &block)
|
111
|
+
should(desc){ skip }
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
def initialize(running_test = nil)
|
117
|
+
@__running_test__ = running_test
|
118
|
+
end
|
119
|
+
|
120
|
+
# check if the assertion is a truthy value, if so create a new pass result, otherwise
|
121
|
+
# create a new fail result with the desc and what failed msg.
|
122
|
+
# all other assertion helpers use this one in the end
|
123
|
+
def assert(assertion, fail_desc=nil, what_failed_msg=nil)
|
124
|
+
what_failed_msg ||= "Failed assert: assertion was <#{assertion.inspect}>."
|
125
|
+
msg = fail_message(fail_desc) { what_failed_msg }
|
126
|
+
assertion ? pass : fail(msg)
|
127
|
+
end
|
128
|
+
|
129
|
+
# the opposite of assert, check if the assertion is a false value, if so create a new pass
|
130
|
+
# result, otherwise create a new fail result with the desc and it's what failed msg
|
131
|
+
def assert_not(assertion, fail_desc=nil)
|
132
|
+
what_failed_msg = "Failed assert_not: assertion was <#{assertion.inspect}>."
|
133
|
+
assert(!assertion, fail_desc, what_failed_msg)
|
134
|
+
end
|
135
|
+
alias_method :refute, :assert_not
|
136
|
+
|
137
|
+
# adds a Skip result to the end of the test's results and breaks test execution
|
138
|
+
def skip(skip_msg=nil)
|
139
|
+
raise(Result::TestSkipped, skip_msg || "")
|
140
|
+
end
|
141
|
+
|
142
|
+
# adds a Pass result to the end of the test's results
|
143
|
+
# does not break test execution
|
144
|
+
def pass(pass_msg=nil)
|
145
|
+
capture_result do |test_name, backtrace|
|
146
|
+
Assert::Result::Pass.new(test_name, pass_msg, backtrace)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# adds a Fail result to the end of the test's results
|
151
|
+
# does not break test execution
|
152
|
+
def fail(fail_msg=nil)
|
153
|
+
capture_result do |test_name, backtrace|
|
154
|
+
message = (fail_message(fail_msg) { }).call
|
155
|
+
Assert::Result::Fail.new(test_name, message, backtrace)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
alias_method :flunk, :fail
|
159
|
+
|
160
|
+
# adds an Ignore result to the end of the test's results
|
161
|
+
# does not break test execution
|
162
|
+
def ignore(ignore_msg=nil)
|
163
|
+
capture_result do |test_name, backtrace|
|
164
|
+
Assert::Result::Ignore.new(test_name, ignore_msg, backtrace)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def subject
|
169
|
+
if subject_block = self.class.subject_block
|
170
|
+
instance_eval(&subject_block)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def inspect
|
175
|
+
"#<#{self.class}>"
|
176
|
+
end
|
177
|
+
|
178
|
+
protected
|
179
|
+
|
180
|
+
def capture_result
|
181
|
+
if block_given?
|
182
|
+
result = yield @__running_test__.name, caller
|
183
|
+
@__running_test__.results << result
|
184
|
+
result
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Returns a Proc that will output a custom message along with the default fail message.
|
189
|
+
def fail_message(fail_desc=nil, &what_failed)
|
190
|
+
fail_desc.kind_of?(::Proc) ? fail_desc : Proc.new do
|
191
|
+
[ what_failed.call, fail_desc ].compact.join("\n")
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Assert
|
2
|
+
module Options
|
3
|
+
|
4
|
+
class Base
|
5
|
+
def method_missing(method, *args, &block)
|
6
|
+
if args.empty?
|
7
|
+
self.instance_variable_get("@#{method}")
|
8
|
+
else
|
9
|
+
value = args.size == 1 ? args.first : args
|
10
|
+
self.instance_variable_set("@#{method}", value)
|
11
|
+
if method.to_s =~ /^default_/
|
12
|
+
self.instance_variable_set("@#{$'}", value)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.included(receiver)
|
20
|
+
receiver.send(:class_variable_set, "@@options", Base.new)
|
21
|
+
receiver.send(:extend, ClassMethods)
|
22
|
+
receiver.send(:include, InstanceMethods)
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
def options(&block)
|
27
|
+
options = self.send(:class_variable_get, "@@options")
|
28
|
+
if block_given?
|
29
|
+
options.instance_eval(&block)
|
30
|
+
else
|
31
|
+
options
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module InstanceMethods
|
37
|
+
def options
|
38
|
+
self.class.options
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
module Assert; end
|
5
|
+
module Assert::RakeTasks
|
6
|
+
|
7
|
+
FILE_SUFFIX = "_test.rb"
|
8
|
+
|
9
|
+
class TestTask < Rake::TaskLib
|
10
|
+
attr_accessor :name, :description, :test_files
|
11
|
+
|
12
|
+
# Create a testing task.
|
13
|
+
def initialize(name=:test)
|
14
|
+
@name = name
|
15
|
+
@description = "Run tests" + (@name==:test ? "" : " for #{@name}")
|
16
|
+
@test_files = []
|
17
|
+
yield self if block_given?
|
18
|
+
end
|
19
|
+
|
20
|
+
# Define the rake task to run this test suite
|
21
|
+
def to_task
|
22
|
+
desc @description
|
23
|
+
task @name do
|
24
|
+
RakeFileUtils.verbose(true) { ruby "\"#{rake_loader}\" " + file_list }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def file_list # :nodoc:
|
29
|
+
@test_files.collect{|f| "\"#{f}\""}.join(' ')
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def rake_loader # :nodoc:
|
35
|
+
find_file('rake/rake_test_loader') or
|
36
|
+
fail "unable to find rake test loader"
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_file(fn) # :nodoc:
|
40
|
+
$LOAD_PATH.each do |path|
|
41
|
+
file_path = File.join(path, "#{fn}.rb")
|
42
|
+
return file_path if File.exist? file_path
|
43
|
+
end
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
def for(test_namespace = :test)
|
50
|
+
self.irb_task(test_namespace.to_s)
|
51
|
+
self.to_tasks(test_namespace.to_s)
|
52
|
+
end
|
53
|
+
|
54
|
+
def irb_task(path)
|
55
|
+
irb_file = File.join(path, "irb.rb")
|
56
|
+
if File.exist?(irb_file)
|
57
|
+
desc "Open irb preloaded with #{irb_file}"
|
58
|
+
task :irb do
|
59
|
+
sh "irb -rubygems -r ./#{irb_file}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_tasks(path)
|
65
|
+
suite_name = File.basename(path)
|
66
|
+
|
67
|
+
# define a rake test task for all top-level test files at this path
|
68
|
+
if !Dir.glob(File.join(path, "**/*#{FILE_SUFFIX}")).empty?
|
69
|
+
TestTask.new(suite_name.to_sym) do |t|
|
70
|
+
file_location = suite_name == path ? '' : " for #{File.join(path.split(File::SEPARATOR)[1..-1])}"
|
71
|
+
t.description = "Run all tests#{file_location}"
|
72
|
+
t.test_files = FileList["#{path}/**/*#{FILE_SUFFIX}"]
|
73
|
+
end.to_task
|
74
|
+
end
|
75
|
+
|
76
|
+
namespace suite_name.to_s do
|
77
|
+
# define rake test tasks for each top-level test file individually
|
78
|
+
Dir.glob(File.join(path, "*#{FILE_SUFFIX}")).each do |test_file|
|
79
|
+
test_name = File.basename(test_file, FILE_SUFFIX)
|
80
|
+
TestTask.new(test_name.to_sym) do |t|
|
81
|
+
t.description = "Run tests for #{[path.split(File::SEPARATOR), test_name].flatten[1..-1].join(':')}"
|
82
|
+
t.test_files = FileList[test_file]
|
83
|
+
end.to_task
|
84
|
+
end
|
85
|
+
|
86
|
+
# recursively define rake test tasks for each file
|
87
|
+
# in each top-level directory
|
88
|
+
Dir.glob(File.join(path, "*/")).each do |test_dir|
|
89
|
+
self.to_tasks(test_dir)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module Assert; end
|
2
|
+
module Assert::Result
|
3
|
+
|
4
|
+
class Base; end
|
5
|
+
class Pass < Base; end
|
6
|
+
class Fail < Base; end
|
7
|
+
class Ignore < Base; end
|
8
|
+
class FromException < Base; end
|
9
|
+
class Error < FromException; end
|
10
|
+
class Skip < FromException; end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def types
|
14
|
+
{ :pass => Pass,
|
15
|
+
:fail => Fail,
|
16
|
+
:ignore => Ignore,
|
17
|
+
:skip => Skip,
|
18
|
+
:error => Error
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Backtrace < ::Array
|
24
|
+
# ripped from minitest...
|
25
|
+
|
26
|
+
file = File.expand_path __FILE__
|
27
|
+
# if RUBY_VERSION =~ /^1\.9/ then # bt's expanded, but __FILE__ isn't :(
|
28
|
+
# File.expand_path __FILE__
|
29
|
+
# elsif __FILE__ =~ /^[^\.]/ then # assume both relative
|
30
|
+
# require 'pathname'
|
31
|
+
# pwd = Pathname.new Dir.pwd
|
32
|
+
# pn = Pathname.new File.expand_path(__FILE__)
|
33
|
+
# relpath = pn.relative_path_from(pwd) rescue pn
|
34
|
+
# pn = File.join ".", relpath unless pn.relative?
|
35
|
+
# pn.to_s
|
36
|
+
# else # assume both are expanded
|
37
|
+
# __FILE__
|
38
|
+
# end
|
39
|
+
|
40
|
+
# './lib' in project dir, or '/usr/local/blahblah' if installed
|
41
|
+
ASSERT_DIR = File.dirname(File.dirname(file))
|
42
|
+
|
43
|
+
def initialize(value=nil)
|
44
|
+
super(value || ["No backtrace"])
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
self.join("\n")
|
49
|
+
end
|
50
|
+
|
51
|
+
def filtered
|
52
|
+
new_bt = []
|
53
|
+
|
54
|
+
self.each do |line|
|
55
|
+
break if line.rindex ASSERT_DIR, 0
|
56
|
+
new_bt << line
|
57
|
+
end
|
58
|
+
|
59
|
+
new_bt = self.reject { |line| line.rindex ASSERT_DIR, 0 } if new_bt.empty?
|
60
|
+
new_bt = self.dup if new_bt.empty?
|
61
|
+
|
62
|
+
self.class.new(new_bt)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# Result classes...
|
68
|
+
|
69
|
+
class Base
|
70
|
+
|
71
|
+
attr_reader :test_name, :message, :backtrace
|
72
|
+
|
73
|
+
def initialize(test_name, message, backtrace=nil)
|
74
|
+
@backtrace = Backtrace.new(backtrace)
|
75
|
+
@test_name = test_name
|
76
|
+
@message = message && !message.empty? ? message : nil
|
77
|
+
end
|
78
|
+
|
79
|
+
Assert::Result.types.keys.each do |meth|
|
80
|
+
define_method("#{meth}?") { false }
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_sym; nil; end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
[self.test_name, self.message, self.trace].compact.join("\n")
|
87
|
+
end
|
88
|
+
|
89
|
+
def trace
|
90
|
+
self.backtrace.filtered.first.to_s
|
91
|
+
end
|
92
|
+
|
93
|
+
def ==(other)
|
94
|
+
self.class == other.class && self.message == other.message
|
95
|
+
end
|
96
|
+
|
97
|
+
def inspect
|
98
|
+
"#<#{self.class} @message=#{self.message.inspect}>"
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
class Pass < Base
|
104
|
+
def pass?; true; end
|
105
|
+
def to_sym; :passed; end
|
106
|
+
|
107
|
+
def to_s
|
108
|
+
"PASS: #{super}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class Fail < Base
|
113
|
+
def fail?; true; end
|
114
|
+
def to_sym; :failed; end
|
115
|
+
|
116
|
+
def to_s
|
117
|
+
"FAIL: #{super}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class Ignore < Base
|
122
|
+
def ignore?; true; end
|
123
|
+
def to_sym; :ignored; end
|
124
|
+
|
125
|
+
def to_s
|
126
|
+
"IGNORE: #{super}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Error and Skip results are built from exceptions being raised
|
131
|
+
class FromException < Base
|
132
|
+
def initialize(test_name, exception)
|
133
|
+
super(test_name, exception.message, exception.backtrace || [])
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# raised by the 'skip' context helper to break test execution
|
138
|
+
class TestSkipped < RuntimeError; end
|
139
|
+
|
140
|
+
class Skip < FromException
|
141
|
+
def skip?; true; end
|
142
|
+
def to_sym; :skipped; end
|
143
|
+
|
144
|
+
def to_s
|
145
|
+
"SKIP: #{super}"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class Error < FromException
|
150
|
+
|
151
|
+
def error?; true; end
|
152
|
+
def to_sym; :errored; end
|
153
|
+
|
154
|
+
def to_s
|
155
|
+
"ERROR: #{super}"
|
156
|
+
end
|
157
|
+
|
158
|
+
# override of the base, always show the full unfiltered backtrace for errors
|
159
|
+
def trace
|
160
|
+
self.backtrace.to_s
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|