speccify 0.3.0 → 0.3.1
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/README.markdown +8 -4
- data/lib/speccify.rb +155 -41
- metadata +4 -4
data/README.markdown
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
# Speccify
|
2
2
|
|
3
|
-
##
|
3
|
+
## The lightweight option.
|
4
4
|
|
5
|
+
[Website](http://speccify.rubyforge.org)
|
6
|
+
[Lighthouse](http://300.lighthouseapp.com/projects/24443/home)
|
7
|
+
[GoogleGroup](http://groups.google.de/group/speccify)
|
8
|
+
[RDoc](http://speccify.rubyforge.org/doc/index.html)
|
5
9
|
|
6
10
|
|
7
11
|
## Features:
|
@@ -18,7 +22,7 @@
|
|
18
22
|
|
19
23
|
### Sophisticated Expectations/Matchers System
|
20
24
|
|
21
|
-
### < 300 LOC
|
25
|
+
### < 300 LOC (sans doc)
|
22
26
|
|
23
27
|
|
24
28
|
|
@@ -71,8 +75,8 @@ Run it with the ruby command:
|
|
71
75
|
|
72
76
|
Use the default rails test directory/structure, testhelpers, assertions and infrastructure.
|
73
77
|
|
74
|
-
1.
|
75
|
-
2.
|
78
|
+
1. require 'speccify' in `test_helper.rb`
|
79
|
+
2. write a spec.
|
76
80
|
3. There is no step three ...
|
77
81
|
|
78
82
|
Tell Speccify what kind of test he is, by passing one of the following options to the describe method:
|
data/lib/speccify.rb
CHANGED
@@ -1,34 +1,40 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
instance_eval { @_result.instance_eval { @run_count ||= 0; @run_count -= 1} if defined?(@_result)}
|
8
|
-
end
|
9
|
-
end # Test::Unit::TestCase
|
2
|
+
# No test/unit default_test whining.
|
3
|
+
Test::Unit::TestCase.class_eval do
|
4
|
+
def default_test # :nodoc:
|
5
|
+
instance_eval { @_result.instance_eval { @run_count ||= 0; @run_count -= 1} if defined?(@_result)}
|
6
|
+
end
|
10
7
|
end
|
11
8
|
|
12
9
|
module Speccify
|
10
|
+
# These method extend the ExampleGroup as class methods.
|
11
|
+
# The methods defined in this module are available
|
12
|
+
# inside the example groups ('describe' blocks).
|
13
13
|
module ExampleGroupClassMethods
|
14
14
|
attr_accessor :desc, :setup_chained, :teardown_chained
|
15
15
|
@desc ||= ""
|
16
16
|
|
17
|
-
def setup_chained
|
17
|
+
def setup_chained # :nodoc:
|
18
18
|
@setup_chained || lambda {}
|
19
19
|
end
|
20
20
|
|
21
|
-
def teardown_chained
|
21
|
+
def teardown_chained # :nodoc:
|
22
22
|
@teardown_chained || lambda {}
|
23
23
|
end
|
24
24
|
|
25
|
+
# ==Run before each Test.
|
26
|
+
# The code in the block attached to this method will be run before each
|
27
|
+
# example in all subsequent, eventually nested example groups.
|
25
28
|
def before(type = :each, &block)
|
26
29
|
raise "unsupported before type: #{type}" unless type == :each
|
27
30
|
passed_through_setup = self.setup_chained
|
28
31
|
self.setup_chained = lambda { instance_eval(&passed_through_setup);instance_eval(&block) }
|
29
32
|
define_method :setup, &self.setup_chained
|
30
33
|
end
|
31
|
-
|
34
|
+
|
35
|
+
# ==Run after each Test.
|
36
|
+
# The code in the block attached to this method will be run after each
|
37
|
+
# example in all subsequent, eventually nested example groups.
|
32
38
|
def after(type = :each, &block)
|
33
39
|
raise "unsupported after type: #{type}" unless type == :each
|
34
40
|
passed_through_teardown = self.teardown_chained
|
@@ -36,71 +42,94 @@ module Speccify
|
|
36
42
|
define_method :teardown, &self.teardown_chained
|
37
43
|
end
|
38
44
|
|
45
|
+
# ==Define an Example Group.
|
46
|
+
# Alike TestCase.
|
39
47
|
def describe desc, &block
|
40
|
-
|
48
|
+
modules = self.ancestors
|
49
|
+
cls = Class.new(self.superclass) do |klass|
|
50
|
+
modules.each {|mod| include mod if (mod.class.to_s == "Module" && !(self < mod))}
|
51
|
+
end
|
41
52
|
Object.const_set self.name + desc.to_s.split(/\W+/).map { |s| s.capitalize }.join, cls
|
42
53
|
cls.setup_chained = self.setup_chained
|
43
54
|
cls.teardown_chained = self.teardown_chained
|
44
55
|
cls.desc = self.desc + " " + desc
|
45
|
-
cls.tests($1.constantize) if defined?(Rails) && self.name =~ /^(.*Controller)Test/
|
56
|
+
cls.tests($1.constantize) if defined?(Rails) && self.name =~ /^(.*(Controller|Helper|Mailer))Test/
|
46
57
|
cls.class_eval(&block)
|
47
58
|
end
|
48
59
|
|
60
|
+
# ==Define a test.
|
61
|
+
# Alike 'def test_whatever ..' test definition.
|
49
62
|
def it desc, &block
|
50
63
|
self.before {}
|
64
|
+
desc = Speccify::Functions.make_constantizeable(desc)
|
51
65
|
define_method "test_#{desc.gsub(/\W+/, '_').downcase}", &lambda {$current_spec = self; instance_eval(&block)} if block_given?
|
52
66
|
self.after {}
|
53
67
|
end
|
54
68
|
end # ExampleGroupClassMethods
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
Speccify::Functions::build_matcher(name, args) do |given, matcher, args|
|
60
|
-
given.send(($1 + "?").to_sym)
|
61
|
-
end
|
62
|
-
else
|
63
|
-
raise NoMethodError.new(name.to_s)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end # ExampleGroupMethods
|
67
|
-
|
69
|
+
|
70
|
+
# == Temporary
|
71
|
+
# A temporary module that holds functionality
|
72
|
+
# that awaits to be refactored to the right place.
|
68
73
|
module Functions
|
69
|
-
def self.determine_class_name(name)
|
74
|
+
def self.determine_class_name(name) #:nodoc:
|
70
75
|
name.to_s.split(/\W+/).map { |s| s[0..0].upcase + s[1..-1] }.join
|
71
76
|
end
|
72
77
|
|
78
|
+
# A matcher in speccify is a builder method that
|
79
|
+
# returns the right matcher object.
|
80
|
+
# This build_matcher method is the internal interface to the
|
81
|
+
# matcher system.
|
82
|
+
# (You should use the def_matcher() method to define matchers.)
|
83
|
+
#
|
84
|
+
# Example:
|
85
|
+
# # The obj.should be_true matcher.
|
86
|
+
# def be_true
|
87
|
+
# Speccify::Functions.build_matcher(:be_true, []) do |given, matcher, args|
|
88
|
+
# given
|
89
|
+
# end
|
90
|
+
# end
|
73
91
|
def self.build_matcher(matcher_name, args, &block)
|
74
92
|
match_block = lambda do |actual, matcher|
|
75
93
|
block.call(actual, matcher, args)
|
76
94
|
end
|
77
95
|
body = lambda do |klass|
|
78
96
|
@matcher_name = matcher_name.to_s
|
79
|
-
def self.matcher_name
|
97
|
+
def self.matcher_name #:nodoc:
|
80
98
|
@matcher_name
|
81
99
|
end
|
82
100
|
|
83
101
|
attr_accessor :positive_msg, :negative_msg, :msgs, :loc
|
84
|
-
def initialize match_block
|
102
|
+
def initialize match_block #:nodoc:
|
85
103
|
@match_block = match_block
|
86
104
|
end
|
87
105
|
|
88
|
-
def method_missing id, *args, &block
|
106
|
+
def method_missing id, *args, &block # :nodoc:
|
89
107
|
require 'ostruct'
|
90
108
|
(self.msgs ||= []) << OpenStruct.new( "name" => id, "args" => args, "block" => block )
|
91
109
|
self
|
92
110
|
end
|
93
111
|
|
94
|
-
def matches? given
|
95
|
-
@positive_msg ||= Speccify::Functions::message(
|
96
|
-
|
112
|
+
def matches? given #:nodoc:
|
113
|
+
@positive_msg ||= Speccify::Functions::message(
|
114
|
+
"#{given} should #{self.class.matcher_name}", "no match", self.class.matcher_name , self.loc || "")
|
115
|
+
@negative_msg ||= Speccify::Functions::message(
|
116
|
+
"#{given} should not #{self.class.matcher_name}", "match", self.class.matcher_name , self.loc || "")
|
97
117
|
@match_block.call(given, self)
|
98
118
|
end
|
99
119
|
end
|
100
120
|
Class.new(&body).new(match_block)
|
101
121
|
end
|
102
122
|
|
103
|
-
def self.
|
123
|
+
def self.make_constantizeable(string)
|
124
|
+
return string unless string.class.to_s == "String"
|
125
|
+
string = string.gsub(/[^\w\s]/,"").gsub(/^[\d\s]*/,"")
|
126
|
+
raise ArgumentError.new(
|
127
|
+
"Invalid argument. Must not be empty after removing '\W'-class chars."
|
128
|
+
) if string.gsub(/\s/,"").empty?
|
129
|
+
string
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.message(expected, actual, op, location) # :nodoc:
|
104
133
|
"""
|
105
134
|
Expected: #{expected.to_s}
|
106
135
|
got: #{actual.to_s}
|
@@ -111,7 +140,7 @@ module Speccify
|
|
111
140
|
end # Functions
|
112
141
|
|
113
142
|
class OperatorMatcherProxy
|
114
|
-
def self.create given, loc, type = true
|
143
|
+
def self.create given, loc, type = true # :nodoc:
|
115
144
|
body = lambda do |klass|
|
116
145
|
define_method(:initialize) do |given|
|
117
146
|
@given = given
|
@@ -122,7 +151,7 @@ module Speccify
|
|
122
151
|
print_given = (@given == nil) ? "nil" : @given
|
123
152
|
print_actual = (type ? "" : "not ") + (actual.nil? ? "nil" : actual.to_s)
|
124
153
|
msg = Speccify::Functions::message(print_actual, print_given, operator, loc)
|
125
|
-
$current_spec.assert(type == @given.send(operator,actual), msg)
|
154
|
+
$current_spec.assert(type == (@given.send(operator,actual) ? true : false), msg)
|
126
155
|
end
|
127
156
|
end
|
128
157
|
end
|
@@ -131,7 +160,14 @@ module Speccify
|
|
131
160
|
end
|
132
161
|
end # OperatorMatcherProxy
|
133
162
|
|
134
|
-
Object
|
163
|
+
class ::Object
|
164
|
+
# == obj.should matcher
|
165
|
+
# Set an expectation to this object. Can be used in conjunction with
|
166
|
+
# an operator (except !=, !~) or a matcher.
|
167
|
+
# ===Examples:
|
168
|
+
# * 1.should == 1
|
169
|
+
# * "asd".should =~ /a/
|
170
|
+
# * nil.should be_nil
|
135
171
|
def should matcher = nil
|
136
172
|
if matcher
|
137
173
|
matcher.loc = caller[0]
|
@@ -140,7 +176,13 @@ module Speccify
|
|
140
176
|
OperatorMatcherProxy.create(self,caller[0])
|
141
177
|
end
|
142
178
|
end
|
143
|
-
|
179
|
+
# == obj.should_not matcher
|
180
|
+
# Set a negative expectation to this object. Can be used in conjunction with
|
181
|
+
# an operator (except !=, !~) or a matcher.
|
182
|
+
# ===Examples:
|
183
|
+
# * 1.should_not == 2
|
184
|
+
# * "asd".should_not =~ /b/
|
185
|
+
# * 1.should_not be_nil
|
144
186
|
def should_not matcher = nil
|
145
187
|
if matcher
|
146
188
|
matcher.loc = caller[0]
|
@@ -152,6 +194,43 @@ module Speccify
|
|
152
194
|
end # Object
|
153
195
|
|
154
196
|
module Extension
|
197
|
+
# == Custom matchers for (almost) FREE
|
198
|
+
#
|
199
|
+
# === A simple example first:
|
200
|
+
#
|
201
|
+
# def_matcher :be_nil do |given, matcher, args|
|
202
|
+
# given.nil?
|
203
|
+
# end
|
204
|
+
# nil.should be_nil
|
205
|
+
# *provide def_matcher() the name of your matcher and attach a block
|
206
|
+
# that defines it's behavior.
|
207
|
+
# *The return value of the block is a boolean that
|
208
|
+
# actually will be expected (should) or not expected (`should_not`).
|
209
|
+
# ==Three arguments are available inside the matcher block:
|
210
|
+
# ===given
|
211
|
+
# The object that has received the should or `should_not`.
|
212
|
+
# === matcher
|
213
|
+
# The matcher object. You can set the failure messages as attributes on this object:
|
214
|
+
# matcher.positive_msg = "You can see me if I am applied to should and I return a false value"
|
215
|
+
# matcher.negative_msg = "You can see me if I am applied to should_not and I return a true value"
|
216
|
+
# It holds a list of all methods that have been called on the matcher (for chaining):
|
217
|
+
#
|
218
|
+
# obj.should matcher_name.some_method(4,5,6) {"and a block"}.second
|
219
|
+
# def_matcher :matcher_name do |given, matcher, args|
|
220
|
+
# # this is an ostruct that holds all information about the first method 'some_method'
|
221
|
+
# matcher.msgs[0]
|
222
|
+
# # this is an ostruct that holds all information about the second method 'second'
|
223
|
+
# matcher.msgs[1]
|
224
|
+
# # this is the name of the first method:
|
225
|
+
# matcher.msgs[0].name #=> :some_method
|
226
|
+
# # this is a list of arguments that have been passed to the first method:
|
227
|
+
# matcher.msgs[0].args #=> [4,5,6]
|
228
|
+
# # this is the block that was attached:
|
229
|
+
# matcher.msgs[0].block #=> proc {"and a block"}
|
230
|
+
# end
|
231
|
+
# === args
|
232
|
+
# A list of all arguments that have been applied to the matcher. Like the 6 in:
|
233
|
+
# (3*3).should_not be(6)
|
155
234
|
def def_matcher(matcher_name, &block)
|
156
235
|
self.class.send :define_method, matcher_name do |*args|
|
157
236
|
Speccify::Functions::build_matcher(matcher_name, args, &block)
|
@@ -162,9 +241,10 @@ module Speccify
|
|
162
241
|
module ::Kernel
|
163
242
|
def describe *args, &block
|
164
243
|
super_class = (Hash === args.last && (args.last[:type] || args.last[:testcase])) || Test::Unit::TestCase
|
165
|
-
super_class.class_eval {extend ExampleGroupClassMethods
|
244
|
+
super_class.class_eval {extend ExampleGroupClassMethods}
|
166
245
|
cls = Class.new(super_class)
|
167
246
|
cnst, desc = args
|
247
|
+
cnst = Speccify::Functions.make_constantizeable(cnst)
|
168
248
|
Object.const_set Speccify::Functions::determine_class_name(cnst.to_s + "Test"), cls
|
169
249
|
cls.desc = String === desc ? desc : cnst.to_s
|
170
250
|
cls.class_eval(&block)
|
@@ -174,11 +254,30 @@ module Speccify
|
|
174
254
|
end # Speccify
|
175
255
|
include Speccify::Extension
|
176
256
|
|
177
|
-
#
|
257
|
+
# same as should == ...
|
178
258
|
def_matcher :be do |given, matcher, args|
|
179
259
|
given == args[0]
|
180
260
|
end
|
181
|
-
|
261
|
+
# expect obj.eql?(arg)
|
262
|
+
def_matcher :eql do |given, matcher, args|
|
263
|
+
given.eql?(args[0])
|
264
|
+
end
|
265
|
+
# expect an exception
|
266
|
+
def_matcher :raise_error do |given, matcher, args|
|
267
|
+
raised = false
|
268
|
+
begin
|
269
|
+
given.call
|
270
|
+
rescue Exception => e
|
271
|
+
raised = true
|
272
|
+
end
|
273
|
+
# todo clean this up
|
274
|
+
raised && (args[0].kind_of?(Regexp) ? e.message =~ args[0] : (args[0].nil? ? true : args[0] == e.message))
|
275
|
+
end
|
276
|
+
# == lambda {do_something}.should change {something}
|
277
|
+
# * lambda {var += 1}.should change {var}.by(1)
|
278
|
+
# * lambda {var += 2}.should change {var}.by_at_least(1)
|
279
|
+
# * lambda {var += 1}.should change {var}.by_at_most(1)
|
280
|
+
# * lambda {var += 2}.should change {var}.from(1).to(3) if var = 1
|
182
281
|
def change(&block)
|
183
282
|
Speccify::Functions::build_matcher(:change, []) do |given, matcher, args|
|
184
283
|
before = block.call
|
@@ -198,4 +297,19 @@ def change(&block)
|
|
198
297
|
matcher.negative_msg = "given block did alter the block attached to change, #{matcher.loc}"
|
199
298
|
comparison
|
200
299
|
end
|
300
|
+
end
|
301
|
+
|
302
|
+
# ==be_*something*
|
303
|
+
# ===This method_missing acts as a matcher builder method.
|
304
|
+
# If a call to be_xyz() reaches this method_missing (say: obj.should be_xyz),
|
305
|
+
# a matcher with the name xyz will be built, whose defining property
|
306
|
+
# is that it returns the value of obj.xyz? for matches?.
|
307
|
+
def method_missing(name, *args, &block)
|
308
|
+
if (name.to_s =~ /^be_(.+)/)
|
309
|
+
Speccify::Functions::build_matcher(name, args) do |given, matcher, args|
|
310
|
+
given.send(($1 + "?").to_sym)
|
311
|
+
end
|
312
|
+
else
|
313
|
+
raise NoMethodError.new(name.to_s)
|
314
|
+
end
|
201
315
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: speccify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthias Hennemeyer
|
@@ -9,11 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-02-
|
12
|
+
date: 2009-02-04 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description: Speccify is a lightweight
|
16
|
+
description: Speccify is a lightweight bdd framework that is built on top of test/unit.
|
17
17
|
email: mhennemeyer@gmail.com
|
18
18
|
executables: []
|
19
19
|
|
@@ -49,6 +49,6 @@ rubyforge_project:
|
|
49
49
|
rubygems_version: 1.3.1
|
50
50
|
signing_key:
|
51
51
|
specification_version: 2
|
52
|
-
summary: Speccify
|
52
|
+
summary: Speccify, the lightweight option.
|
53
53
|
test_files: []
|
54
54
|
|