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.
Files changed (3) hide show
  1. data/README.markdown +8 -4
  2. data/lib/speccify.rb +155 -41
  3. metadata +4 -4
@@ -1,7 +1,11 @@
1
1
  # Speccify
2
2
 
3
- ## A lightweight alternative to RSpec.
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. sudo use_minitest yes
75
- 2. require 'speccify' in test_helper.rb
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:
@@ -1,34 +1,40 @@
1
1
  require 'test/unit'
2
-
3
-
4
- unless defined?(MiniTest)
5
- Test::Unit::TestCase.class_eval do
6
- def default_test
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
- cls = Class.new(self.superclass)
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
- module ExampleGroupMethods
57
- def method_missing(name, *args, &block)
58
- if (name.to_s =~ /^be_(.+)/)
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("#{given} should #{self.class.matcher_name}", "no match", self.class.matcher_name , self.loc || "")
96
- @negative_msg ||= Speccify::Functions::message("#{given} should not #{self.class.matcher_name}", "match", self.class.matcher_name , self.loc || "")
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.message(expected, actual, op, location)
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.class_eval do
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; include ExampleGroupMethods}
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
- # A few matchers:
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.0
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-01 00:00:00 +01:00
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 alternative to RSpec.
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 is a lightweight alternative to RSpec.
52
+ summary: Speccify, the lightweight option.
53
53
  test_files: []
54
54