jnunemaker-matchy 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/License.txt +20 -0
- data/Manifest.txt +36 -0
- data/PostInstall.txt +7 -0
- data/README.rdoc +162 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +73 -0
- data/config/requirements.rb +15 -0
- data/countloc.rb +67 -0
- data/lib/matchy.rb +19 -0
- data/lib/matchy/built_in/change_expectations.rb +31 -0
- data/lib/matchy/built_in/enumerable_expectations.rb +41 -0
- data/lib/matchy/built_in/error_expectations.rb +66 -0
- data/lib/matchy/built_in/operator_expectations.rb +42 -0
- data/lib/matchy/built_in/truth_expectations.rb +146 -0
- data/lib/matchy/custom_matcher.rb +10 -0
- data/lib/matchy/expectation_builder.rb +9 -0
- data/lib/matchy/matcher_builder.rb +51 -0
- data/lib/matchy/modals.rb +34 -0
- data/lib/matchy/version.rb +9 -0
- data/matchy.gemspec +26 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/test/all.rb +7 -0
- data/test/ruby1.9.compatibility_tests.rb +541 -0
- data/test/test_change_expectation.rb +63 -0
- data/test/test_custom_matcher.rb +139 -0
- data/test/test_enumerable_expectations.rb +91 -0
- data/test/test_error_expectations.rb +144 -0
- data/test/test_expectation_builder.rb +28 -0
- data/test/test_helper.rb +1 -0
- data/test/test_matcher_builder.rb +72 -0
- data/test/test_modals.rb +39 -0
- data/test/test_operator_expectations.rb +157 -0
- data/test/test_truth_expectations.rb +373 -0
- metadata +109 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
module Matchy
|
2
|
+
module Expectations
|
3
|
+
module TestCaseExtensions
|
4
|
+
|
5
|
+
# Calls +include?+ on the receiver for any object. You can also provide
|
6
|
+
# multiple arguments to see if all of them are included.
|
7
|
+
#
|
8
|
+
# ==== Examples
|
9
|
+
#
|
10
|
+
# [1,2,3].should include(1)
|
11
|
+
# [7,8,8].should_not include(3)
|
12
|
+
# ['a', 'b', 'c'].should include('a', 'c')
|
13
|
+
#
|
14
|
+
def include(*obj)
|
15
|
+
_clude(:include, obj)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Expects the receiver to exclude the given object(s). You can provide
|
19
|
+
# multiple arguments to see if all of them are included.
|
20
|
+
#
|
21
|
+
# ==== Examples
|
22
|
+
#
|
23
|
+
# [1,2,3].should exclude(16)
|
24
|
+
# [7,8,8].should_not exclude(7)
|
25
|
+
# ['a', 'b', 'c'].should exclude('e', 'f', 'g')
|
26
|
+
#
|
27
|
+
def exclude(*obj)
|
28
|
+
_clude(:exclude, obj)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def _clude(sym, obj)
|
33
|
+
build_matcher(sym, obj) do |given, matcher, args|
|
34
|
+
matcher.positive_failure_message = "Expected #{given.inspect} to #{sym} #{args.inspect}."
|
35
|
+
matcher.negative_failure_message = "Expected #{given.inspect} to not #{sym} #{args.inspect}."
|
36
|
+
args.inject(true) {|m,o| m && (given.include?(o) == (sym == :include)) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Matchy
|
2
|
+
module Expectations
|
3
|
+
module TestCaseExtensions
|
4
|
+
# Expects a lambda to raise an error. You can specify the error or leave it blank to encompass
|
5
|
+
# any error.
|
6
|
+
#
|
7
|
+
# ==== Examples
|
8
|
+
#
|
9
|
+
# lambda { raise "FAILURE." }.should raise_error
|
10
|
+
# lambda { puts i_dont_exist }.should raise_error(NameError)
|
11
|
+
#
|
12
|
+
def raise_error(*obj)
|
13
|
+
build_matcher(:raise_error, obj) do |receiver, matcher, args|
|
14
|
+
expected = args[0] || Exception
|
15
|
+
raised = false
|
16
|
+
error = nil
|
17
|
+
begin
|
18
|
+
receiver.call
|
19
|
+
rescue Exception => e
|
20
|
+
raised = true
|
21
|
+
error = e
|
22
|
+
end
|
23
|
+
if expected.respond_to?(:ancestors) && expected.ancestors.include?(Exception)
|
24
|
+
matcher.positive_failure_message = "Expected #{receiver.inspect} to raise #{expected.name}, " +
|
25
|
+
(error ? "but #{error.class.name} was raised instead." : "but none was raised.")
|
26
|
+
matcher.negative_failure_message = "Expected #{receiver.inspect} to not raise #{expected.name}."
|
27
|
+
comparison = (raised && error.class.ancestors.include?(expected))
|
28
|
+
else
|
29
|
+
message = error ? error.message : "none"
|
30
|
+
matcher.positive_failure_message = "Expected #{receiver.inspect} to raise error with message matching '#{expected}', but '#{message}' was raised."
|
31
|
+
matcher.negative_failure_message = "Expected #{receiver.inspect} to raise error with message not matching '#{expected}', but '#{message}' was raised."
|
32
|
+
comparison = (raised && (expected.kind_of?(Regexp) ? ((error.message =~ expected) ? true : false) : expected == error.message))
|
33
|
+
end
|
34
|
+
comparison
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Expects a lambda to throw an error.
|
39
|
+
#
|
40
|
+
# ==== Examples
|
41
|
+
#
|
42
|
+
# lambda { throw :thing }.should throw_symbol(:thing)
|
43
|
+
# lambda { "not this time" }.should_not throw_symbol(:hello)
|
44
|
+
#
|
45
|
+
def throw_symbol(*obj)
|
46
|
+
build_matcher(:throw_symbol, obj) do |receiver, matcher, args|
|
47
|
+
raised, thrown_symbol, expected = false, nil, args[0]
|
48
|
+
begin
|
49
|
+
receiver.call
|
50
|
+
rescue NameError => e
|
51
|
+
raise e unless e.message =~ /uncaught throw/
|
52
|
+
raised = true
|
53
|
+
thrown_symbol = e.name.to_sym if e.respond_to?(:name)
|
54
|
+
rescue ArgumentError => e
|
55
|
+
raise e unless e.message =~ /uncaught throw/
|
56
|
+
thrown_symbol = e.message.match(/uncaught throw :(.+)/)[1].to_sym
|
57
|
+
end
|
58
|
+
matcher.positive_failure_message = "Expected #{receiver.inspect} to throw :#{expected}, but " +
|
59
|
+
"#{thrown_symbol ? ':' + thrown_symbol.to_s + ' was thrown instead' : 'no symbol was thrown'}."
|
60
|
+
matcher.negative_failure_message = "Expected #{receiver.inspect} to not throw :#{expected}."
|
61
|
+
expected == thrown_symbol
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Matchy
|
2
|
+
module Expectations
|
3
|
+
# Class to handle operator expectations.
|
4
|
+
#
|
5
|
+
# ==== Examples
|
6
|
+
#
|
7
|
+
# 13.should == 13
|
8
|
+
# "hello".length.should_not == 2
|
9
|
+
#
|
10
|
+
class OperatorExpectation #< Base
|
11
|
+
include Test::Unit::Assertions
|
12
|
+
|
13
|
+
def initialize(receiver, match)
|
14
|
+
@receiver, @match = receiver, match
|
15
|
+
end
|
16
|
+
|
17
|
+
['==', '===', '=~', '>', '>=', '<', '<='].each do |op|
|
18
|
+
define_method(op) do |expected|
|
19
|
+
@expected = expected
|
20
|
+
(@receiver.send(op,expected) ? true : false) == @match ? pass! : fail!(op)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
def pass!
|
26
|
+
defined?($current_test_case) ? $current_test_case.assert(true) : (assert true)
|
27
|
+
end
|
28
|
+
|
29
|
+
def fail!(operator)
|
30
|
+
flunk @match ? failure_message(operator) : negative_failure_message(operator)
|
31
|
+
end
|
32
|
+
|
33
|
+
def failure_message(operator)
|
34
|
+
"Expected #{@receiver.inspect} to #{operator} #{@expected.inspect}."
|
35
|
+
end
|
36
|
+
|
37
|
+
def negative_failure_message(operator)
|
38
|
+
"Expected #{@receiver.inspect} to not #{operator} #{@expected.inspect}."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module Matchy
|
2
|
+
module Expectations
|
3
|
+
module TestCaseExtensions
|
4
|
+
# Simply checks if the receiver matches the expected object.
|
5
|
+
# TODO: Fill this out to implement much of the RSpec functionality (and then some)
|
6
|
+
#
|
7
|
+
# ==== Examples
|
8
|
+
#
|
9
|
+
# "hello".should be("hello")
|
10
|
+
# (13 < 20).should be(true)
|
11
|
+
#
|
12
|
+
def be(*obj)
|
13
|
+
build_matcher(:be, obj) do |receiver, matcher, args|
|
14
|
+
@receiver, expected = receiver, args[0]
|
15
|
+
matcher.positive_failure_message = "Expected #{@receiver.inspect} to be #{expected.inspect}."
|
16
|
+
matcher.negative_failure_message = "Expected #{@receiver.inspect} to not be #{expected.inspect}."
|
17
|
+
expected == @receiver
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Checks if the given object is within a given object and delta.
|
22
|
+
#
|
23
|
+
# ==== Examples
|
24
|
+
#
|
25
|
+
# (20.0 - 2.0).should be_close(18.0)
|
26
|
+
# (13.0 - 4.0).should be_close(9.0, 0.5)
|
27
|
+
#
|
28
|
+
def be_close(obj, delta = 0.3)
|
29
|
+
build_matcher(:be_close, [obj, delta]) do |receiver, matcher, args|
|
30
|
+
@receiver, expected, delta = receiver, args[0], args[1]
|
31
|
+
matcher.positive_failure_message = "Expected #{@receiver.inspect} to be close to #{expected.inspect} (delta: #{delta})."
|
32
|
+
matcher.negative_failure_message = "Expected #{@receiver.inspect} to not be close to #{expected.inspect} (delta: #{delta})."
|
33
|
+
(@receiver - expected).abs < delta
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Calls +exist?+ on the given object.
|
38
|
+
#
|
39
|
+
# ==== Examples
|
40
|
+
#
|
41
|
+
# # found_user.exist?
|
42
|
+
# found_user.should exist
|
43
|
+
#
|
44
|
+
def exist
|
45
|
+
ask_for(:exist, :with_arg => nil)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Calls +eql?+ on the given object (i.e., are the objects the same value?)
|
49
|
+
#
|
50
|
+
# ==== Examples
|
51
|
+
#
|
52
|
+
# 1.should_not eql(1.0)
|
53
|
+
# (12 / 6).should eql(6)
|
54
|
+
#
|
55
|
+
def eql(*obj)
|
56
|
+
ask_for(:eql, :with_arg => obj)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Calls +equal?+ on the given object (i.e., do the two objects have the same +object_id+?)
|
60
|
+
#
|
61
|
+
# ==== Examples
|
62
|
+
#
|
63
|
+
# x = [1,2,3]
|
64
|
+
# y = [1,2,3]
|
65
|
+
#
|
66
|
+
# # Different object_id's...
|
67
|
+
# x.should_not equal(y)
|
68
|
+
#
|
69
|
+
# # The same object_id
|
70
|
+
# x[0].should equal(y[0])
|
71
|
+
#
|
72
|
+
def equal(*obj)
|
73
|
+
ask_for(:equal, :with_arg => obj)
|
74
|
+
end
|
75
|
+
|
76
|
+
# A last ditch way to implement your testing logic. You probably shouldn't use this unless you
|
77
|
+
# have to.
|
78
|
+
#
|
79
|
+
# ==== Examples
|
80
|
+
#
|
81
|
+
# (13 - 4).should satisfy(lambda {|i| i < 20})
|
82
|
+
# "hello".should_not satisfy(lambda {|s| s =~ /hi/})
|
83
|
+
#
|
84
|
+
def satisfy(*obj)
|
85
|
+
build_matcher(:satisfy, obj) do |receiver, matcher, args|
|
86
|
+
@receiver, expected = receiver, args[0]
|
87
|
+
matcher.positive_failure_message = "Expected #{@receiver.inspect} to satisfy given block."
|
88
|
+
matcher.negative_failure_message = "Expected #{@receiver.inspect} to not satisfy given block."
|
89
|
+
expected.call(@receiver) == true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Checks if the given object responds to the given method
|
94
|
+
#
|
95
|
+
# ==== Examples
|
96
|
+
#
|
97
|
+
# "foo".should respond_to(:length)
|
98
|
+
# {}.should respond_to(:has_key?)
|
99
|
+
def respond_to(*meth)
|
100
|
+
ask_for(:respond_to, :with_arg => meth)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Asks given for success?().
|
104
|
+
# This is necessary because Rails Integration::Session
|
105
|
+
# overides method_missing without grace.
|
106
|
+
#
|
107
|
+
# ==== Examples
|
108
|
+
#
|
109
|
+
# @response.should be_success
|
110
|
+
def be_success
|
111
|
+
ask_for(:success, :with_arg => nil)
|
112
|
+
end
|
113
|
+
|
114
|
+
alias_method :old_missing, :method_missing
|
115
|
+
# ==be_*something(*args)
|
116
|
+
#
|
117
|
+
# ===This method_missing acts as a matcher builder.
|
118
|
+
# If a call to be_xyz() reaches this method_missing (say: obj.should be_xyz),
|
119
|
+
# a matcher with the name xyz will be built, whose defining property
|
120
|
+
# is that it returns the value of obj.xyz? for matches?.
|
121
|
+
# ==== Examples
|
122
|
+
#
|
123
|
+
# nil.should be_nil
|
124
|
+
# 17.should be_kind_of(Fixnum)
|
125
|
+
# obj.something? #=> true
|
126
|
+
# obj.should be_something
|
127
|
+
def method_missing(name, *args, &block)
|
128
|
+
if (name.to_s =~ /^be_(.+)/)
|
129
|
+
ask_for($1, :with_arg => args)
|
130
|
+
else
|
131
|
+
old_missing(name, *args, &block)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
def ask_for(sym, option={})
|
137
|
+
build_matcher(sym, (option[:with_arg] || [])) do |receiver, matcher, args|
|
138
|
+
expected, meth = args[0], (sym.to_s + "?" ).to_sym
|
139
|
+
matcher.positive_failure_message = "Expected #{receiver.inspect} to return true for #{sym}?, with '#{(expected && expected.inspect) || 'no args'}'."
|
140
|
+
matcher.negative_failure_message = "Expected #{receiver.inspect} to not return true for #{sym}?, with '#{(expected && expected.inspect) || 'no args'}'."
|
141
|
+
expected ? receiver.send(meth, expected) : receiver.send(meth)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Matchy
|
2
|
+
module MatcherBuilder
|
3
|
+
class ChainedMessage < Struct.new(:name, :args, :block); end
|
4
|
+
|
5
|
+
def build_matcher(matcher_name=nil, args=[], &block)
|
6
|
+
match_block = lambda do |actual, matcher|
|
7
|
+
block.call(actual, matcher, args)
|
8
|
+
end
|
9
|
+
|
10
|
+
body = lambda do |klass|
|
11
|
+
include Test::Unit::Assertions
|
12
|
+
@matcher_name = matcher_name.to_s
|
13
|
+
|
14
|
+
def self.matcher_name
|
15
|
+
@matcher_name
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :matcher_name
|
19
|
+
attr_accessor :positive_failure_message, :negative_failure_message, :chained_messages
|
20
|
+
|
21
|
+
def initialize(match_block, test_case)
|
22
|
+
@match_block, @test_case = match_block, test_case
|
23
|
+
@matcher_name = self.class.matcher_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(id, *args, &block)
|
27
|
+
(self.chained_messages ||= []) << ChainedMessage.new(id, args, block)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def matches?(given)
|
32
|
+
@positive_failure_message ||= "Matching with '#{matcher_name}' failed, although it should match."
|
33
|
+
@negative_failure_message ||= "Matching with '#{matcher_name}' passed, although it should_not match."
|
34
|
+
@match_block.call(given, self)
|
35
|
+
end
|
36
|
+
|
37
|
+
def fail!(which)
|
38
|
+
@test_case.flunk(which ? failure_message : negative_failure_message)
|
39
|
+
end
|
40
|
+
|
41
|
+
def pass!(which)
|
42
|
+
@test_case.assert true
|
43
|
+
end
|
44
|
+
|
45
|
+
alias_method :failure_message, :positive_failure_message
|
46
|
+
end
|
47
|
+
|
48
|
+
Class.new(&body).new(match_block, self)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Matchy
|
2
|
+
module Modals
|
3
|
+
# Tests an expectation against the given object.
|
4
|
+
#
|
5
|
+
# ==== Examples
|
6
|
+
#
|
7
|
+
# "hello".should eql("hello")
|
8
|
+
# 13.should equal(13)
|
9
|
+
# lambda { raise "u r doomed" }.should raise_error
|
10
|
+
#
|
11
|
+
def should(expectation = nil)
|
12
|
+
Matchy::ExpectationBuilder.build_expectation(true, expectation, self)
|
13
|
+
end
|
14
|
+
|
15
|
+
alias :will :should
|
16
|
+
|
17
|
+
# Tests that an expectation doesn't match the given object.
|
18
|
+
#
|
19
|
+
# ==== Examples
|
20
|
+
#
|
21
|
+
# "hello".should_not eql("hi")
|
22
|
+
# 41.should_not equal(13)
|
23
|
+
# lambda { "savd bai da bell" }.should_not raise_error
|
24
|
+
#
|
25
|
+
def should_not(expectation = nil)
|
26
|
+
Matchy::ExpectationBuilder.build_expectation(false, expectation, self)
|
27
|
+
end
|
28
|
+
|
29
|
+
alias :will_not :should_not
|
30
|
+
alias :wont :should_not
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Object.send(:include, Matchy::Modals)
|
data/matchy.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = %q{jnunemaker-matchy}
|
3
|
+
s.version = "0.4.0"
|
4
|
+
s.authors = ["Jeremy McAnally"]
|
5
|
+
s.date = %q{2009-03-23}
|
6
|
+
s.description = %q{RSpec-esque matchers for use in Test::Unit}
|
7
|
+
s.email = ["jeremy@entp.com"]
|
8
|
+
s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc"]
|
9
|
+
s.files = ["History.txt", "License.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "config/hoe.rb", "config/requirements.rb", "countloc.rb", "lib/matchy.rb", "lib/matchy/built_in/change_expectations.rb", "lib/matchy/built_in/enumerable_expectations.rb", "lib/matchy/built_in/error_expectations.rb", "lib/matchy/built_in/operator_expectations.rb", "lib/matchy/built_in/truth_expectations.rb", "lib/matchy/custom_matcher.rb", "lib/matchy/expectation_builder.rb", "lib/matchy/matcher_builder.rb", "lib/matchy/modals.rb", "lib/matchy/version.rb", "matchy.gemspec", "setup.rb", "tasks/deployment.rake", "tasks/environment.rake", "test/all.rb", "test/ruby1.9.compatibility_tests.rb", "test/test_change_expectation.rb", "test/test_custom_matcher.rb", "test/test_enumerable_expectations.rb", "test/test_error_expectations.rb", "test/test_expectation_builder.rb", "test/test_helper.rb", "test/test_matcher_builder.rb", "test/test_modals.rb", "test/test_operator_expectations.rb", "test/test_truth_expectations.rb"]
|
10
|
+
s.has_rdoc = true
|
11
|
+
s.homepage = %q{http://matchy.rubyforge.org}
|
12
|
+
s.post_install_message = %q{
|
13
|
+
For more information on matchy, see http://matchy.rubyforge.org
|
14
|
+
|
15
|
+
NOTE: Change this information in PostInstall.txt
|
16
|
+
You can also delete it if you don't want it.
|
17
|
+
|
18
|
+
}
|
19
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
20
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.rdoc"]
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
s.rubyforge_project = %q{matchy}
|
23
|
+
s.rubygems_version = %q{1.3.1}
|
24
|
+
s.summary = %q{RSpec-esque matchers for use in Test::Unit}
|
25
|
+
s.test_files = ["test/test_change_expectation.rb", "test/test_custom_matcher.rb", "test/test_enumerable_expectations.rb", "test/test_error_expectations.rb", "test/test_expectation_builder.rb", "test/test_helper.rb", "test/test_matcher_builder.rb", "test/test_modals.rb", "test/test_operator_expectations.rb", "test/test_truth_expectations.rb"]
|
26
|
+
end
|